From 1709ffbc18da917e758eb43fb8135667be057563 Mon Sep 17 00:00:00 2001 From: mroihn Date: Tue, 10 Dec 2024 01:22:32 +0700 Subject: [PATCH 1/3] feat: Get user teams and Get team by id --- src/controllers/api.controller.ts | 2 ++ src/controllers/team.controller.ts | 20 ++++++++++++++++ src/repositories/team.repository.ts | 12 +++++++++- src/routes/team.route.ts | 36 +++++++++++++++++++++++++++-- src/types/team.type.ts | 8 +++++++ 5 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 src/controllers/team.controller.ts diff --git a/src/controllers/api.controller.ts b/src/controllers/api.controller.ts index bb281b8..4dd599c 100644 --- a/src/controllers/api.controller.ts +++ b/src/controllers/api.controller.ts @@ -3,6 +3,7 @@ import { authProtectedRouter, authRouter } from './auth.controller'; import { healthRouter } from './health.controller'; import { mediaRouter } from './media.controller'; import { teamMemberProtectedRouter } from './team-member.controller'; +import { teamProtectedRouter } from './team.controller'; const unprotectedApiRouter = new OpenAPIHono(); unprotectedApiRouter.route('/', healthRouter); @@ -12,6 +13,7 @@ const protectedApiRouter = new OpenAPIHono(); protectedApiRouter.route('/', authProtectedRouter); protectedApiRouter.route('/', mediaRouter); protectedApiRouter.route('/', teamMemberProtectedRouter); +protectedApiRouter.route('/', teamProtectedRouter); export const apiRouter = new OpenAPIHono(); apiRouter.route('/', unprotectedApiRouter); diff --git a/src/controllers/team.controller.ts b/src/controllers/team.controller.ts new file mode 100644 index 0000000..bc57d3e --- /dev/null +++ b/src/controllers/team.controller.ts @@ -0,0 +1,20 @@ +import { db } from '~/db/drizzle'; +import { createAuthRouter} from '~/utils/router-factory'; +import { getTeamByIdRoute, getTeamsRoute } from '~/routes/team.route'; +import { getTeamById, getUserTeams } from '~/repositories/team.repository'; +export const teamProtectedRouter = createAuthRouter(); + +teamProtectedRouter.openapi(getTeamByIdRoute, async (c) => { + const { teamId } = c.req.valid('param'); + const team = await getTeamById(db, teamId, { + teamMember: true, + competition: true, + }); + return c.json(team, 200); +}); + +teamProtectedRouter.openapi(getTeamsRoute, async (c) => { + const user = c.var.user; + const teams = await getUserTeams(db, user.id); + return c.json(teams, 200); +}); diff --git a/src/repositories/team.repository.ts b/src/repositories/team.repository.ts index 2c91202..359d6d4 100644 --- a/src/repositories/team.repository.ts +++ b/src/repositories/team.repository.ts @@ -1,6 +1,6 @@ import { eq } from 'drizzle-orm'; import type { Database } from '~/db/drizzle'; -import { team } from '~/db/schema'; +import { team, teamMember, user, competition } from '~/db/schema'; import type { TeamMemberRelationOption } from './team-member.repository'; interface TeamRelationOption { @@ -36,3 +36,13 @@ export const getTeamById = async ( }, }); }; + +export const getUserTeams = async (db: Database, userId: string) => { + return await db + .select({ team }) + .from(team) + .innerJoin(teamMember, eq(team.id, teamMember.teamId)) + .innerJoin(user, eq(teamMember.userId, user.id)) + .innerJoin(competition, eq(team.competitionId, competition.id)) + .where(eq(user.id, userId)); +}; diff --git a/src/routes/team.route.ts b/src/routes/team.route.ts index f8ee937..09b6c1e 100644 --- a/src/routes/team.route.ts +++ b/src/routes/team.route.ts @@ -1,4 +1,11 @@ import { createRoute } from '@hono/zod-openapi'; +import { team } from '~/db/schema'; +import { + ListUserTeamSchema, + TeamSchema, + TeamIdParam, +} from '~/types/team.type'; +import { createErrorResponse } from '~/utils/error-response-factory'; export const joinTeamByCodeRoute = createRoute({ operationId: 'joinTeamByCode', @@ -13,7 +20,18 @@ export const getTeamsRoute = createRoute({ tags: ['team'], method: 'get', path: '/team', - responses: {}, + responses: { + 200: { + description: 'Get user teams', + content: { + 'application/json': { + schema: ListUserTeamSchema, + }, + }, + }, + 400: createErrorResponse('UNION', 'Bad request error'), + 500: createErrorResponse('GENERIC', 'Internal server error'), + }, }); export const getTeamByIdRoute = createRoute({ @@ -21,7 +39,21 @@ export const getTeamByIdRoute = createRoute({ tags: ['team'], method: 'get', path: '/team/{teamId}', - responses: {}, + request: { + params: TeamIdParam, + }, + responses: { + 200: { + description: 'Get team by id', + content: { + 'application/json': { + schema: TeamSchema, + }, + }, + }, + 400: createErrorResponse('UNION', 'Bad request error'), + 500: createErrorResponse('GENERIC', 'Internal server error'), + }, }); export const postCreateTeamRoute = createRoute({ diff --git a/src/types/team.type.ts b/src/types/team.type.ts index 791546b..fad8c4b 100644 --- a/src/types/team.type.ts +++ b/src/types/team.type.ts @@ -1,3 +1,11 @@ +import { createSelectSchema } from 'drizzle-zod'; import { z } from 'zod'; +import { team } from '~/db/schema'; export const TeamIdParam = z.object({ teamId: z.string() }); + +export const TeamSchema = createSelectSchema(team, { + createdAt: z.union([z.string(), z.date()]), +}).openapi('Team'); + +export const ListUserTeamSchema = z.array(TeamSchema); From fec48c6ab172487fa4c406fd1c2d89370a2be0d2 Mon Sep 17 00:00:00 2001 From: mroihn Date: Tue, 10 Dec 2024 02:48:06 +0700 Subject: [PATCH 2/3] fix: missing variable for response --- src/repositories/team.repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repositories/team.repository.ts b/src/repositories/team.repository.ts index 359d6d4..df888ac 100644 --- a/src/repositories/team.repository.ts +++ b/src/repositories/team.repository.ts @@ -39,7 +39,7 @@ export const getTeamById = async ( export const getUserTeams = async (db: Database, userId: string) => { return await db - .select({ team }) + .select({ team, competition }) .from(team) .innerJoin(teamMember, eq(team.id, teamMember.teamId)) .innerJoin(user, eq(teamMember.userId, user.id)) From eb51e2c26079ccb0e30473ecbb1b7f5dc57f5d09 Mon Sep 17 00:00:00 2001 From: mroihn Date: Fri, 13 Dec 2024 23:33:13 +0700 Subject: [PATCH 3/3] fix:type --- src/controllers/team.controller.ts | 9 ++++++++- src/repositories/team.repository.ts | 7 ++++++- src/types/competition.type.ts | 5 +++++ src/types/media.type.ts | 4 +++- src/types/team.type.ts | 11 +++++++++-- 5 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 src/types/competition.type.ts diff --git a/src/controllers/team.controller.ts b/src/controllers/team.controller.ts index bc57d3e..9bb581b 100644 --- a/src/controllers/team.controller.ts +++ b/src/controllers/team.controller.ts @@ -7,8 +7,15 @@ export const teamProtectedRouter = createAuthRouter(); teamProtectedRouter.openapi(getTeamByIdRoute, async (c) => { const { teamId } = c.req.valid('param'); const team = await getTeamById(db, teamId, { - teamMember: true, + teamMember: { + user: true, + nisn: true, + kartu: true, + poster: true, + twibbon: true, + }, competition: true, + paymentProof: false, }); return c.json(team, 200); }); diff --git a/src/repositories/team.repository.ts b/src/repositories/team.repository.ts index df888ac..3ffafed 100644 --- a/src/repositories/team.repository.ts +++ b/src/repositories/team.repository.ts @@ -38,11 +38,16 @@ export const getTeamById = async ( }; export const getUserTeams = async (db: Database, userId: string) => { - return await db + const teams = await db .select({ team, competition }) .from(team) .innerJoin(teamMember, eq(team.id, teamMember.teamId)) .innerJoin(user, eq(teamMember.userId, user.id)) .innerJoin(competition, eq(team.competitionId, competition.id)) .where(eq(user.id, userId)); + + return teams.map((item) => ({ + ...item.team, + competition: item.competition, + })); }; diff --git a/src/types/competition.type.ts b/src/types/competition.type.ts new file mode 100644 index 0000000..563fc4c --- /dev/null +++ b/src/types/competition.type.ts @@ -0,0 +1,5 @@ +import { createSelectSchema } from 'drizzle-zod'; +import { z } from 'zod'; +import { competition } from '~/db/schema'; + +export const CompetitionSchema = createSelectSchema(competition).openapi('Competition'); \ No newline at end of file diff --git a/src/types/media.type.ts b/src/types/media.type.ts index cc6d2b8..f3dcfff 100644 --- a/src/types/media.type.ts +++ b/src/types/media.type.ts @@ -2,7 +2,9 @@ import { createSelectSchema } from 'drizzle-zod'; import { z } from 'zod'; import { media, mediaBucketEnum } from '~/db/schema/media.schema'; -export const MediaSchema = createSelectSchema(media).openapi('Media'); +export const MediaSchema = createSelectSchema(media, { + createdAt: z.union([z.string(), z.date()]), +}).openapi('Media'); export const GetPresignedLinkQuerySchema = z.object({ filename: z.string().openapi({ diff --git a/src/types/team.type.ts b/src/types/team.type.ts index fad8c4b..7a81eaf 100644 --- a/src/types/team.type.ts +++ b/src/types/team.type.ts @@ -1,11 +1,18 @@ import { createSelectSchema } from 'drizzle-zod'; import { z } from 'zod'; import { team } from '~/db/schema'; +import { TeamMemberSchema } from './team-member.type'; +import { CompetitionSchema } from './competition.type'; export const TeamIdParam = z.object({ teamId: z.string() }); export const TeamSchema = createSelectSchema(team, { createdAt: z.union([z.string(), z.date()]), -}).openapi('Team'); +}) + .extend({ + teamMembers: z.array(TeamMemberSchema).optional(), + competition: CompetitionSchema.optional(), + }) + .openapi('Team'); -export const ListUserTeamSchema = z.array(TeamSchema); +export const ListUserTeamSchema = z.array(TeamSchema); \ No newline at end of file