From 7b7f3de44e9a7d1b8bb9575ad760d216f3e76c04 Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Wed, 6 Nov 2024 02:03:08 +0000 Subject: [PATCH 1/2] feat(be): apply sorting on admin get-contest-submissions api --- .../enum/contest-submission-order.enum.ts | 8 +++++ .../src/submission/submission.resolver.ts | 30 ++++++++-------- .../src/submission/submission.service.ts | 35 +++++++++++++++++-- .../pipe/src/contest-submission-order.pipe.ts | 24 +++++++++++++ apps/backend/libs/pipe/src/index.ts | 1 + .../Get Contest Submissions/Succeed.bru | 23 ++++++++---- 6 files changed, 99 insertions(+), 22 deletions(-) create mode 100644 apps/backend/apps/admin/src/submission/enum/contest-submission-order.enum.ts create mode 100644 apps/backend/libs/pipe/src/contest-submission-order.pipe.ts diff --git a/apps/backend/apps/admin/src/submission/enum/contest-submission-order.enum.ts b/apps/backend/apps/admin/src/submission/enum/contest-submission-order.enum.ts new file mode 100644 index 0000000000..37a8306fce --- /dev/null +++ b/apps/backend/apps/admin/src/submission/enum/contest-submission-order.enum.ts @@ -0,0 +1,8 @@ +export enum ContestSubmissionOrder { + studentIdASC = 'studentId-asc', + studentIdDESC = 'studentId-desc', + realNameASC = 'realName-asc', + realNameDESC = 'realName-desc', + usernameASC = 'username-asc', + usernameDESC = 'username-desc' +} diff --git a/apps/backend/apps/admin/src/submission/submission.resolver.ts b/apps/backend/apps/admin/src/submission/submission.resolver.ts index 91831bd8bb..809b92cc2f 100644 --- a/apps/backend/apps/admin/src/submission/submission.resolver.ts +++ b/apps/backend/apps/admin/src/submission/submission.resolver.ts @@ -1,7 +1,7 @@ -import { InternalServerErrorException, Logger } from '@nestjs/common' import { Args, Int, Query, Resolver } from '@nestjs/graphql' -import { CursorValidationPipe } from '@libs/pipe' +import { ContestSubmissionOrderPipe, CursorValidationPipe } from '@libs/pipe' import { Submission } from '@admin/@generated' +import { ContestSubmissionOrder } from './enum/contest-submission-order.enum' import { ContestSubmission } from './model/contest-submission.model' import { GetContestSubmissionsInput } from './model/get-contest-submission.input' import { SubmissionDetail } from './model/submission-detail.output' @@ -9,7 +9,6 @@ import { SubmissionService } from './submission.service' @Resolver(() => Submission) export class SubmissionResolver { - private readonly logger = new Logger(SubmissionResolver.name) constructor(private readonly submissionService: SubmissionService) {} /** @@ -28,19 +27,22 @@ export class SubmissionResolver { @Args('cursor', { nullable: true, type: () => Int }, CursorValidationPipe) cursor: number | null, @Args('take', { nullable: true, defaultValue: 10, type: () => Int }) - take: number + take: number, + @Args( + 'order', + { nullable: true, type: () => String }, + ContestSubmissionOrderPipe + ) + order?: ContestSubmissionOrder ): Promise { - try { - return await this.submissionService.getContestSubmissions( - input, - take, - cursor - ) - } catch (error) { - this.logger.error(error.error) - throw new InternalServerErrorException() - } + return await this.submissionService.getContestSubmissions( + input, + take, + cursor, + order + ) } + /** * 특정 Contest의 특정 제출 내역에 대한 상세 정보를 불러옵니다. */ diff --git a/apps/backend/apps/admin/src/submission/submission.service.ts b/apps/backend/apps/admin/src/submission/submission.service.ts index 90e1f8e6de..6e83c5c347 100644 --- a/apps/backend/apps/admin/src/submission/submission.service.ts +++ b/apps/backend/apps/admin/src/submission/submission.service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@nestjs/common' +import type { Prisma } from '@prisma/client' import { plainToInstance } from 'class-transformer' import { EntityNotExistException } from '@libs/exception' import { PrismaService } from '@libs/prisma' import type { Language, ResultStatus } from '@admin/@generated' import { Snippet } from '@admin/problem/model/template.input' +import { ContestSubmissionOrder } from './enum/contest-submission-order.enum' import type { GetContestSubmissionsInput } from './model/get-contest-submission.input' @Injectable() @@ -13,7 +15,8 @@ export class SubmissionService { async getContestSubmissions( input: GetContestSubmissionsInput, take: number, - cursor: number | null + cursor: number | null, + order?: ContestSubmissionOrder ) { const paginator = this.prisma.getPaginator(cursor) @@ -59,7 +62,8 @@ export class SubmissionService { } } } - } + }, + orderBy: order ? this.getOrderBy(order) : undefined }) const results = contestSubmissions.map((c) => { @@ -84,6 +88,33 @@ export class SubmissionService { return results } + getOrderBy( + order: ContestSubmissionOrder + ): Prisma.SubmissionOrderByWithRelationInput { + const [attr, value] = order.split('-') + + switch (order) { + case ContestSubmissionOrder.studentIdASC: + case ContestSubmissionOrder.studentIdDESC: + case ContestSubmissionOrder.usernameASC: + case ContestSubmissionOrder.usernameDESC: + return { + user: { + [attr]: value + } + } + case ContestSubmissionOrder.realNameASC: + case ContestSubmissionOrder.realNameDESC: + return { + user: { + userProfile: { + [attr]: value + } + } + } + } + } + async getSubmission(id: number) { const submission = await this.prisma.submission.findFirst({ where: { diff --git a/apps/backend/libs/pipe/src/contest-submission-order.pipe.ts b/apps/backend/libs/pipe/src/contest-submission-order.pipe.ts new file mode 100644 index 0000000000..f0187c3f5f --- /dev/null +++ b/apps/backend/libs/pipe/src/contest-submission-order.pipe.ts @@ -0,0 +1,24 @@ +import { + BadRequestException, + Injectable, + type PipeTransform +} from '@nestjs/common' +import { ContestSubmissionOrder } from '@admin/submission/enum/contest-submission-order.enum' + +@Injectable() +export class ContestSubmissionOrderPipe implements PipeTransform { + transform(value: unknown) { + if (!value) { + return undefined + } else if ( + !Object.values(ContestSubmissionOrder).includes( + value as ContestSubmissionOrder + ) + ) { + throw new BadRequestException( + 'Contest-submission-order validation failed' + ) + } + return value + } +} diff --git a/apps/backend/libs/pipe/src/index.ts b/apps/backend/libs/pipe/src/index.ts index 51df868689..fb1fa3d2bd 100644 --- a/apps/backend/libs/pipe/src/index.ts +++ b/apps/backend/libs/pipe/src/index.ts @@ -3,3 +3,4 @@ export * from './id-validation.pipe' export * from './group-id.pipe' export * from './required-int.pipe' export * from './problem-order.pipe' +export * from './contest-submission-order.pipe' diff --git a/collection/admin/Submission/Get Contest Submissions/Succeed.bru b/collection/admin/Submission/Get Contest Submissions/Succeed.bru index d7f3942608..0194d64067 100644 --- a/collection/admin/Submission/Get Contest Submissions/Succeed.bru +++ b/collection/admin/Submission/Get Contest Submissions/Succeed.bru @@ -14,12 +14,14 @@ body:graphql { query GetContestSubmissions( $input: GetContestSubmissionsInput!, $cursor: Int, - $take: Int + $take: Int, + $order: String ) { getContestSubmissions( input: $input, cursor: $cursor, - take: $take + take: $take, + order: $order ) { title studentId @@ -44,7 +46,8 @@ body:graphql:vars { "problemId": 1, "searchingName": "lee" }, - "take": 10 + "take": 10, + "order": "username-desc" } } @@ -55,13 +58,21 @@ docs { - https://github.com/skkuding/codedang/pull/1924 #### 필요 인자 - | `input` | `take` | `cursor` | - |----------|--------|----------| - | 밑에서 설명 | Pagination 구현을 위함 | Pagination 구현을 위함 | + | `input` | `take` | `cursor` |`order`| + |----------|--------|----------|--------| + | 밑에서 설명 | Pagination 구현을 위함 | Pagination 구현을 위함 | 정렬 | `input` - `contestId`: 제출 내역을 불러올 Contest의 ID - `problemId?`: 제출된 내역 중 특정 Problem의 제출만 필터링 - `searchingName?`: 필터링할 User의 realName(없으면 모든 유저) + `order` + - `studentId-asc`: 학번 오름차순 정렬 + - `studentId-desc`: 학번 내림차순 정렬 + - `realName-asc`: 학생이름 오름차순 정렬 + - `realName-desc`: 학생이름 내림차순 정렬 + - `username-asc`: username 오름차순 정렬 + - `username-desc`: username 내림차순 정렬 + } From b39ad7388c3f75a4abdc2a7ee8cc3e14c128776e Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Wed, 6 Nov 2024 03:41:23 +0000 Subject: [PATCH 2/2] fix(be): make order nullable --- apps/backend/apps/admin/src/submission/submission.resolver.ts | 2 +- apps/backend/apps/admin/src/submission/submission.service.ts | 2 +- apps/backend/libs/pipe/src/contest-submission-order.pipe.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/backend/apps/admin/src/submission/submission.resolver.ts b/apps/backend/apps/admin/src/submission/submission.resolver.ts index 809b92cc2f..d401dcd4d0 100644 --- a/apps/backend/apps/admin/src/submission/submission.resolver.ts +++ b/apps/backend/apps/admin/src/submission/submission.resolver.ts @@ -33,7 +33,7 @@ export class SubmissionResolver { { nullable: true, type: () => String }, ContestSubmissionOrderPipe ) - order?: ContestSubmissionOrder + order: ContestSubmissionOrder | null ): Promise { return await this.submissionService.getContestSubmissions( input, diff --git a/apps/backend/apps/admin/src/submission/submission.service.ts b/apps/backend/apps/admin/src/submission/submission.service.ts index 6e83c5c347..2c23699f4d 100644 --- a/apps/backend/apps/admin/src/submission/submission.service.ts +++ b/apps/backend/apps/admin/src/submission/submission.service.ts @@ -16,7 +16,7 @@ export class SubmissionService { input: GetContestSubmissionsInput, take: number, cursor: number | null, - order?: ContestSubmissionOrder + order: ContestSubmissionOrder | null ) { const paginator = this.prisma.getPaginator(cursor) diff --git a/apps/backend/libs/pipe/src/contest-submission-order.pipe.ts b/apps/backend/libs/pipe/src/contest-submission-order.pipe.ts index f0187c3f5f..42a93b5205 100644 --- a/apps/backend/libs/pipe/src/contest-submission-order.pipe.ts +++ b/apps/backend/libs/pipe/src/contest-submission-order.pipe.ts @@ -9,7 +9,7 @@ import { ContestSubmissionOrder } from '@admin/submission/enum/contest-submissio export class ContestSubmissionOrderPipe implements PipeTransform { transform(value: unknown) { if (!value) { - return undefined + return null } else if ( !Object.values(ContestSubmissionOrder).includes( value as ContestSubmissionOrder