From 5bebf337db88857aad0a5412bd08ad0f0537c9da Mon Sep 17 00:00:00 2001 From: percyfikri Date: Fri, 13 Sep 2024 10:33:09 +0700 Subject: [PATCH] Update : API for users (get, post, patch) --- apps/backend/src/routes/users/route.ts | 122 ++++++++++++++++--------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/apps/backend/src/routes/users/route.ts b/apps/backend/src/routes/users/route.ts index 6de294e..26a13e1 100644 --- a/apps/backend/src/routes/users/route.ts +++ b/apps/backend/src/routes/users/route.ts @@ -26,23 +26,24 @@ export const userFormSchema = z.object({ address: z.string().min(1), phoneNumber: z.string().min(1).max(13), isEnabled: z.string().default("false"), - roles: z - .string() - .refine( - (data) => { - console.log(data); - try { - const parsed = JSON.parse(data); - return Array.isArray(parsed); - } catch { - return false; - } - }, - { - message: "Roles must be an array", - } - ) - .optional(), + // roles: z + // .string() + // .refine( + // (data) => { + // console.log(data); + // try { + // const parsed = JSON.parse(data); + // return Array.isArray(parsed); + // } catch { + // return false; + // } + // }, + // { + // message: "Roles must be an array", + // } + // ) + // .optional(), + roles: z.array(z.string()).optional(), }); export const userUpdateSchema = userFormSchema.extend({ @@ -58,6 +59,7 @@ const usersRoute = new Hono() * - includeTrashed: boolean (default: false)\ * - withMetadata: boolean */ + .get( "/", checkPermission("users.readAll"), @@ -73,17 +75,18 @@ const usersRoute = new Hono() .optional() .transform((v) => v?.toLowerCase() === "true"), page: z.coerce.number().int().min(0).default(0), - limit: z.coerce.number().int().min(1).max(1000).default(1), + limit: z.coerce.number().int().min(1).max(1000).default(100), q: z.string().default(""), }) ), async (c) => { const { includeTrashed, page, limit, q } = c.req.valid("query"); - + + // Total count for pagination const totalCountQuery = includeTrashed ? sql`(SELECT count(*) FROM ${users})` : sql`(SELECT count(*) FROM ${users} WHERE ${users.deletedAt} IS NULL)`; - + const result = await db .select({ id: users.id, @@ -95,7 +98,10 @@ const usersRoute = new Hono() updatedAt: users.updatedAt, ...(includeTrashed ? { deletedAt: users.deletedAt } : {}), company: respondents.companyName, - roles: rolesSchema.name, + role: { + name: rolesSchema.name, + id: rolesSchema.id, + }, fullCount: totalCountQuery, }) .from(users) @@ -118,8 +124,47 @@ const usersRoute = new Hono() .offset(page * limit) .limit(limit); + // Group roles for each user to prevent duplication + const userMap = new Map(); + + result.forEach((item) => { + if (!userMap.has(item.id)) { + userMap.set(item.id, { + id: item.id, + name: item.name, + email: item.email ?? null, + username: item.username, + isEnabled: item.isEnabled ?? false, + createdAt: item.createdAt ?? new Date(), + updatedAt: item.updatedAt ?? new Date(), + deletedAt: item.deletedAt ?? undefined, + company: item.company, + roles: item.role ? [{ id: item.role.id, name: item.role.name }] : [], + }); + } else { + const existingUser = userMap.get(item.id); + if (item.role) { + existingUser?.roles.push({ id: item.role.id, name: item.role.name }); + } + } + }); + + // Return user data without duplication and roles in array form + const groupedData = Array.from(userMap.values()); + return c.json({ - data: result.map((d) => ({ ...d, fullCount: undefined })), + data: groupedData.map((d) => ({ ...d, fullCount: undefined })), _metadata: { currentPage: page, totalPages: Math.ceil( @@ -130,7 +175,8 @@ const usersRoute = new Hono() }, }); } - ) + ) + //get user by id .get( "/:id", @@ -277,31 +323,17 @@ const usersRoute = new Hono() message: "Error creating respondent: " + err.message, }); }); - - // Automatically assign "user" role to the new user - const [role] = await trx - .select() - .from(rolesSchema) - .where(eq(rolesSchema.code, "user")) - .limit(1); - - if (!role) throw notFound(); - - await trx.insert(rolesToUsers).values({ - userId: newUser.id, - roleId: role.id, - }); // Add other roles if provided if (userData.roles) { - const roles = JSON.parse(userData.roles) as string[]; + const roles = userData.roles; - for (let roleCode of roles) { + for (let roleId of roles) { const role = ( await trx .select() .from(rolesSchema) - .where(eq(rolesSchema.code, roleCode)) + .where(eq(rolesSchema.id, roleId)) .limit(1) )[0]; @@ -312,7 +344,7 @@ const usersRoute = new Hono() }); } else { throw new HTTPException(404, { - message: `Role ${roleCode} does not exists`, + message: `Role ${roleId} does not exists`, }); } } @@ -400,18 +432,18 @@ const usersRoute = new Hono() // Update roles if provided if (userData.roles) { - const roles = JSON.parse(userData.roles) as string[]; + const roles = userData.roles; // Remove existing roles for the user await trx.delete(rolesToUsers).where(eq(rolesToUsers.userId, userId)); // Assign new roles - for (let roleCode of roles) { + for (let roleId of roles) { const role = ( await trx .select() .from(rolesSchema) - .where(eq(rolesSchema.code, roleCode)) + .where(eq(rolesSchema.id, roleId)) .limit(1) )[0]; @@ -422,7 +454,7 @@ const usersRoute = new Hono() }); } else { throw new HTTPException(404, { - message: `Role ${roleCode} does not exist`, + message: `Role ${roleId} does not exist`, }); } }