diff --git a/apps/backend/apps/admin/src/contest/contest.resolver.ts b/apps/backend/apps/admin/src/contest/contest.resolver.ts
index 8db19415fe..7a1c48d016 100644
--- a/apps/backend/apps/admin/src/contest/contest.resolver.ts
+++ b/apps/backend/apps/admin/src/contest/contest.resolver.ts
@@ -2,10 +2,8 @@ import { ParseBoolPipe } from '@nestjs/common'
import { Args, Context, Int, Mutation, Query, Resolver } from '@nestjs/graphql'
import { Contest, ContestProblem } from '@generated'
import { AuthenticatedRequest, UseRolesGuard } from '@libs/auth'
-import { OPEN_SPACE_ID } from '@libs/constants'
import {
CursorValidationPipe,
- GroupIDPipe,
IDValidationPipe,
RequiredIntPipe
} from '@libs/pipe'
@@ -33,16 +31,10 @@ export class ContestResolver {
new RequiredIntPipe('take')
)
take: number,
- @Args(
- 'groupId',
- { defaultValue: OPEN_SPACE_ID, type: () => Int },
- GroupIDPipe
- )
- groupId: number,
@Args('cursor', { nullable: true, type: () => Int }, CursorValidationPipe)
cursor: number | null
) {
- return await this.contestService.getContests(take, groupId, cursor)
+ return await this.contestService.getContests(take, cursor)
}
@Query(() => ContestWithParticipants)
@@ -56,35 +48,25 @@ export class ContestResolver {
@Mutation(() => Contest)
async createContest(
@Args('input') input: CreateContestInput,
- @Args(
- 'groupId',
- { defaultValue: OPEN_SPACE_ID, type: () => Int },
- GroupIDPipe
- )
- groupId: number,
@Context('req') req: AuthenticatedRequest
) {
- return await this.contestService.createContest(groupId, req.user.id, input)
+ return await this.contestService.createContest(req.user.id, input)
}
@Mutation(() => Contest)
- async updateContest(
- @Args('groupId', { type: () => Int }, GroupIDPipe) groupId: number,
- @Args('input') input: UpdateContestInput
- ) {
- return await this.contestService.updateContest(groupId, input)
+ async updateContest(@Args('input') input: UpdateContestInput) {
+ return await this.contestService.updateContest(input)
}
@Mutation(() => Contest)
async deleteContest(
- @Args('groupId', { type: () => Int }, GroupIDPipe) groupId: number,
@Args('contestId', { type: () => Int }) contestId: number
) {
- return await this.contestService.deleteContest(groupId, contestId)
+ return await this.contestService.deleteContest(contestId)
}
/**
- * Contest의 소속 Group을 Open Space(groupId === 1)로 이동시키기 위한 요청(Publicizing Requests)들을 불러옵니다.
+ * Contest를 공개(Open Space)로 이동시키기 위한 요청(Publicizing Requests)들을 불러옵니다.
* @returns Publicizing Request 배열
*/
@Query(() => [PublicizingRequest])
@@ -94,24 +76,19 @@ export class ContestResolver {
}
/**
- * Contest의 소속 Group을 Open Space(groupId === 1)로 이동시키기 위한 요청(Publicizing Request)를 생성합니다.
- * @param groupId Contest가 속한 Group의 ID. 이미 Open Space(groupId === 1)이 아니어야 합니다.
+ * Contest를 공개(Open Space)로 이동시키기 위한 요청(Publicizing Request)을 생성합니다.
* @param contestId Contest의 ID
* @returns 생성된 Publicizing Request
*/
@Mutation(() => PublicizingRequest)
async createPublicizingRequest(
- @Args('groupId', { type: () => Int }, GroupIDPipe) groupId: number,
@Args('contestId', { type: () => Int }) contestId: number
) {
- return await this.contestService.createPublicizingRequest(
- groupId,
- contestId
- )
+ return await this.contestService.createPublicizingRequest(contestId)
}
/**
- * Contest의 소속 Group을 Open Space(groupId === 1)로 이동시키기 위한 요청(Publicizing Request)을 처리합니다.
+ * Contest를 공개(Open Space)로 이동시키기 위한 요청(Publicizing Request)을 처리합니다.
* @param contestId Publicizing Request를 생성한 contest의 Id
* @param isAccepted 요청 수락 여부
* @returns
@@ -130,13 +107,11 @@ export class ContestResolver {
@Mutation(() => [ContestProblem])
async importProblemsToContest(
- @Args('groupId', { type: () => Int }, GroupIDPipe) groupId: number,
@Args('contestId', { type: () => Int }) contestId: number,
@Args('problemIdsWithScore', { type: () => [ProblemScoreInput] })
problemIdsWithScore: ProblemScoreInput[]
) {
return await this.contestService.importProblemsToContest(
- groupId,
contestId,
problemIdsWithScore
)
@@ -144,13 +119,11 @@ export class ContestResolver {
@Mutation(() => [ContestProblem])
async removeProblemsFromContest(
- @Args('groupId', { type: () => Int }, GroupIDPipe) groupId: number,
@Args('contestId', { type: () => Int })
contestId: number,
@Args('problemIds', { type: () => [Int] }) problemIds: number[]
) {
return await this.contestService.removeProblemsFromContest(
- groupId,
contestId,
problemIds
)
@@ -188,16 +161,11 @@ export class ContestResolver {
@Mutation(() => DuplicatedContestResponse)
async duplicateContest(
- @Args('groupId', { type: () => Int }, GroupIDPipe) groupId: number,
@Args('contestId', { type: () => Int })
contestId: number,
@Context('req') req: AuthenticatedRequest
) {
- return await this.contestService.duplicateContest(
- groupId,
- contestId,
- req.user.id
- )
+ return await this.contestService.duplicateContest(contestId, req.user.id)
}
/**
diff --git a/apps/backend/apps/admin/src/contest/contest.service.spec.ts b/apps/backend/apps/admin/src/contest/contest.service.spec.ts
index 6b92bc7f3d..4f213e14fb 100644
--- a/apps/backend/apps/admin/src/contest/contest.service.spec.ts
+++ b/apps/backend/apps/admin/src/contest/contest.service.spec.ts
@@ -1,6 +1,6 @@
import { CACHE_MANAGER } from '@nestjs/cache-manager'
import { Test, type TestingModule } from '@nestjs/testing'
-import { ContestProblem, Group, ContestRecord } from '@generated'
+import { ContestProblem, ContestRecord } from '@generated'
import { Problem } from '@generated'
import { Contest } from '@generated'
import { faker } from '@faker-js/faker'
@@ -20,7 +20,6 @@ import type { PublicizingRequest } from './model/publicizing-request.model'
const contestId = 1
const userId = 1
-const groupId = 1
const problemId = 2
const startTime = faker.date.past()
const endTime = faker.date.future()
@@ -36,7 +35,6 @@ const problemIdsWithScore = {
const contest: Contest = {
id: contestId,
createdById: userId,
- groupId,
title: 'title',
description: 'description',
penalty: 20,
@@ -63,7 +61,6 @@ const contest: Contest = {
const contestWithCount = {
id: contestId,
createdById: userId,
- groupId,
title: 'title',
description: 'description',
penalty: 20,
@@ -93,7 +90,6 @@ const contestWithCount = {
const contestWithParticipants: ContestWithParticipants = {
id: contestId,
createdById: userId,
- groupId,
title: 'title',
description: 'description',
penalty: 20,
@@ -118,20 +114,6 @@ const contestWithParticipants: ContestWithParticipants = {
benefits: 'benefits'
}
-const group: Group = {
- id: groupId,
- groupName: 'groupName',
- description: 'description',
- config: {
- showOnList: true,
- allowJoinFromSearch: true,
- allowJoinWithURL: false,
- requireApprovalBeforeJoin: true
- },
- createTime: faker.date.past(),
- updateTime: faker.date.past()
-}
-
const problem: Problem = {
id: problemId,
createdById: 2,
@@ -257,9 +239,6 @@ const db = {
updateMany: stub().resolves([Problem]),
findFirstOrThrow: stub().resolves(Problem)
},
- group: {
- findUnique: stub().resolves(Group)
- },
submission: {
findMany: stub().resolves([submissionsWithProblemTitleAndUsername])
},
@@ -310,7 +289,7 @@ describe('ContestService', () => {
it('should return an array of contests', async () => {
db.contest.findMany.resolves([contestWithCount])
- const res = await service.getContests(5, 2, 0)
+ const res = await service.getContests(5, 0)
expect(res).to.deep.equal([contestWithParticipants])
})
})
@@ -328,9 +307,8 @@ describe('ContestService', () => {
describe('createContest', () => {
it('should return created contest', async () => {
db.contest.create.resolves(contest)
- db.group.findUnique.resolves(group)
- const res = await service.createContest(groupId, userId, input)
+ const res = await service.createContest(userId, input)
expect(res).to.deep.equal(contest)
})
})
@@ -340,15 +318,9 @@ describe('ContestService', () => {
db.contest.findFirst.resolves(contest)
db.contest.update.resolves(contest)
- const res = await service.updateContest(groupId, updateInput)
+ const res = await service.updateContest(updateInput)
expect(res).to.deep.equal(contest)
})
-
- it('should throw error when groupId or contestId not exist', async () => {
- expect(service.updateContest(1000, updateInput)).to.be.rejectedWith(
- EntityNotExistException
- )
- })
})
describe('deleteContest', () => {
@@ -356,12 +328,12 @@ describe('ContestService', () => {
db.contest.findFirst.resolves(contest)
db.contest.delete.resolves(contest)
- const res = await service.deleteContest(groupId, contestId)
+ const res = await service.deleteContest(contestId)
expect(res).to.deep.equal(contest)
})
- it('should throw error when groupId or contestId not exist', async () => {
- expect(service.deleteContest(1000, 1000)).to.be.rejectedWith(
+ it('should throw error when contestId not exist', async () => {
+ expect(service.deleteContest(1000)).to.be.rejectedWith(
EntityNotExistException
)
})
@@ -381,7 +353,7 @@ describe('ContestService', () => {
})
})
- it('should throw error when groupId or contestId not exist', async () => {
+ it('should throw error when contestId not exist', async () => {
expect(service.handlePublicizingRequest(1000, true)).to.be.rejectedWith(
EntityNotExistException
)
@@ -407,9 +379,7 @@ describe('ContestService', () => {
db.contestProblem.findFirst.resolves(null)
const res = await Promise.all(
- await service.importProblemsToContest(groupId, contestId, [
- problemIdsWithScore
- ])
+ await service.importProblemsToContest(contestId, [problemIdsWithScore])
)
expect(res).to.deep.equal([contestProblem])
@@ -420,7 +390,7 @@ describe('ContestService', () => {
db.problem.update.resolves(problem)
db.contestProblem.findFirst.resolves(ContestProblem)
- const res = await service.importProblemsToContest(groupId, contestId, [
+ const res = await service.importProblemsToContest(contestId, [
problemIdsWithScore
])
@@ -429,7 +399,7 @@ describe('ContestService', () => {
it('should throw error when the contestId not exist', async () => {
expect(
- service.importProblemsToContest(groupId, 9999, [problemIdsWithScore])
+ service.importProblemsToContest(9999, [problemIdsWithScore])
).to.be.rejectedWith(EntityNotExistException)
})
})
diff --git a/apps/backend/apps/admin/src/contest/contest.service.ts b/apps/backend/apps/admin/src/contest/contest.service.ts
index d2a8ad0583..d95bca4205 100644
--- a/apps/backend/apps/admin/src/contest/contest.service.ts
+++ b/apps/backend/apps/admin/src/contest/contest.service.ts
@@ -1,14 +1,9 @@
import { CACHE_MANAGER } from '@nestjs/cache-manager'
-import {
- Inject,
- Injectable,
- UnprocessableEntityException
-} from '@nestjs/common'
+import { Inject, Injectable } from '@nestjs/common'
import { Contest, ResultStatus, Submission } from '@generated'
import type { ContestProblem } from '@prisma/client'
import { Cache } from 'cache-manager'
import {
- OPEN_SPACE_ID,
PUBLICIZING_REQUEST_EXPIRE_TIME,
PUBLICIZING_REQUEST_KEY,
MIN_DATE,
@@ -34,12 +29,11 @@ export class ContestService {
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache
) {}
- async getContests(take: number, groupId: number, cursor: number | null) {
+ async getContests(take: number, cursor: number | null) {
const paginator = this.prisma.getPaginator(cursor)
const contests = await this.prisma.contest.findMany({
...paginator,
- where: { groupId },
take,
include: {
// eslint-disable-next-line @typescript-eslint/naming-convention
@@ -84,7 +78,6 @@ export class ContestService {
}
async createContest(
- groupId: number,
userId: number,
contest: CreateContestInput
): Promise {
@@ -94,20 +87,10 @@ export class ContestService {
)
}
- const group = await this.prisma.group.findUnique({
- where: {
- id: groupId
- }
- })
- if (!group) {
- throw new EntityNotExistException('Group')
- }
-
try {
return await this.prisma.contest.create({
data: {
createdById: userId,
- groupId,
...contest
}
})
@@ -116,14 +99,10 @@ export class ContestService {
}
}
- async updateContest(
- groupId: number,
- contest: UpdateContestInput
- ): Promise {
+ async updateContest(contest: UpdateContestInput): Promise {
const contestFound = await this.prisma.contest.findFirst({
where: {
- id: contest.id,
- groupId
+ id: contest.id
},
select: {
startTime: true,
@@ -214,11 +193,10 @@ export class ContestService {
}
}
- async deleteContest(groupId: number, contestId: number) {
+ async deleteContest(contestId: number) {
const contest = await this.prisma.contest.findFirst({
where: {
- id: contestId,
- groupId
+ id: contestId
},
select: {
contestProblem: {
@@ -236,7 +214,7 @@ export class ContestService {
(problem) => problem.problemId
)
if (problemIds.length) {
- await this.removeProblemsFromContest(groupId, contestId, problemIds)
+ await this.removeProblemsFromContest(contestId, problemIds)
}
try {
@@ -299,9 +277,7 @@ export class ContestService {
where: {
id: contestId
},
- data: {
- groupId: OPEN_SPACE_ID
- }
+ data: {}
})
} catch (error) {
throw new UnprocessableDataException(error.message)
@@ -314,17 +290,10 @@ export class ContestService {
} as PublicizingResponse
}
- async createPublicizingRequest(groupId: number, contestId: number) {
- if (groupId == OPEN_SPACE_ID) {
- throw new UnprocessableEntityException(
- 'This contest is already publicized'
- )
- }
-
+ async createPublicizingRequest(contestId: number) {
const contest = await this.prisma.contest.findFirst({
where: {
- id: contestId,
- groupId
+ id: contestId
}
})
if (!contest) {
@@ -360,14 +329,12 @@ export class ContestService {
}
async importProblemsToContest(
- groupId: number,
contestId: number,
problemIdsWithScore: ProblemScoreInput[]
) {
const contest = await this.prisma.contest.findUnique({
where: {
- id: contestId,
- groupId
+ id: contestId
},
include: {
submission: {
@@ -442,15 +409,10 @@ export class ContestService {
return contestProblems
}
- async removeProblemsFromContest(
- groupId: number,
- contestId: number,
- problemIds: number[]
- ) {
+ async removeProblemsFromContest(contestId: number, problemIds: number[]) {
const contest = await this.prisma.contest.findUnique({
where: {
- id: contestId,
- groupId
+ id: contestId
},
include: {
submission: {
@@ -608,18 +570,16 @@ export class ContestService {
/**
* Duplicate contest with contest problems and users who participated in the contest
* Not copied: submission
- * @param groupId group to duplicate contest
* @param contestId contest to duplicate
* @param userId user who tries to duplicates the contest
* @returns
*/
- async duplicateContest(groupId: number, contestId: number, userId: number) {
+ async duplicateContest(contestId: number, userId: number) {
const [contestFound, contestProblemsFound, userContestRecords] =
await Promise.all([
this.prisma.contest.findFirst({
where: {
- id: contestId,
- groupId
+ id: contestId
}
}),
this.prisma.contestProblem.findMany({
@@ -658,7 +618,6 @@ export class ContestService {
...contestDataToCopy,
title: 'Copy of ' + title,
createdById: userId,
- groupId,
isVisible: newVisible
}
})
diff --git a/apps/backend/apps/admin/src/problem/mock/mock.ts b/apps/backend/apps/admin/src/problem/mock/mock.ts
index a39bd61adc..c0d644672e 100644
--- a/apps/backend/apps/admin/src/problem/mock/mock.ts
+++ b/apps/backend/apps/admin/src/problem/mock/mock.ts
@@ -375,7 +375,6 @@ export const exampleContest: Contest = {
description: 'example',
penalty: 20,
lastPenalty: false,
- groupId: 1,
createdById: 1,
isVisible: true,
isRankVisible: true,
diff --git a/apps/backend/apps/admin/src/problem/problem.service.ts b/apps/backend/apps/admin/src/problem/problem.service.ts
index 0043ff1e55..9063536f5b 100644
--- a/apps/backend/apps/admin/src/problem/problem.service.ts
+++ b/apps/backend/apps/admin/src/problem/problem.service.ts
@@ -605,7 +605,7 @@ export class ProblemService {
contestId: number
): Promise[]> {
await this.prisma.contest.findFirstOrThrow({
- where: { id: contestId, groupId }
+ where: { id: contestId }
})
const contestProblems = await this.prisma.contestProblem.findMany({
where: { contestId }
@@ -619,7 +619,7 @@ export class ProblemService {
problemIdsWithScore: ProblemScoreInput[]
): Promise[]> {
await this.prisma.contest.findFirstOrThrow({
- where: { id: contestId, groupId }
+ where: { id: contestId }
})
const queries = problemIdsWithScore.map((record) => {
@@ -644,7 +644,7 @@ export class ProblemService {
orders: number[]
): Promise[]> {
await this.prisma.contest.findFirstOrThrow({
- where: { id: contestId, groupId }
+ where: { id: contestId }
})
const contestProblems = await this.prisma.contestProblem.findMany({
diff --git a/apps/backend/apps/client/src/announcement/announcement.controller.ts b/apps/backend/apps/client/src/announcement/announcement.controller.ts
index 82038e4885..9bf96ba0dc 100644
--- a/apps/backend/apps/client/src/announcement/announcement.controller.ts
+++ b/apps/backend/apps/client/src/announcement/announcement.controller.ts
@@ -44,8 +44,7 @@ export class AnnouncementController {
} else {
if (contestId) {
return await this.announcementService.getContestAnnouncements(
- contestId,
- groupId
+ contestId
)
} else {
return await this.announcementService.getAssignmentAnnouncements(
diff --git a/apps/backend/apps/client/src/announcement/announcement.service.spec.ts b/apps/backend/apps/client/src/announcement/announcement.service.spec.ts
index be12cf43fb..7c0102f1c6 100644
--- a/apps/backend/apps/client/src/announcement/announcement.service.spec.ts
+++ b/apps/backend/apps/client/src/announcement/announcement.service.spec.ts
@@ -70,7 +70,7 @@ describe('AnnouncementService', () => {
describe('getContestAnnouncements', () => {
it('should return multiple contest announcements', async () => {
- const res = await service.getContestAnnouncements(1, 1)
+ const res = await service.getContestAnnouncements(1)
expect(res)
.excluding(['createTime', 'updateTime', 'content'])
.to.deep.equal([
diff --git a/apps/backend/apps/client/src/announcement/announcement.service.ts b/apps/backend/apps/client/src/announcement/announcement.service.ts
index 3f4818c707..679c8c3e1c 100644
--- a/apps/backend/apps/client/src/announcement/announcement.service.ts
+++ b/apps/backend/apps/client/src/announcement/announcement.service.ts
@@ -6,15 +6,11 @@ import { PrismaService } from '@libs/prisma'
export class AnnouncementService {
constructor(private readonly prisma: PrismaService) {}
- async getContestAnnouncements(
- contestId: number,
- groupId: number
- ): Promise {
+ async getContestAnnouncements(contestId: number): Promise {
const { contestProblem, announcement } =
await this.prisma.contest.findUniqueOrThrow({
where: {
- id: contestId,
- groupId
+ id: contestId
},
select: {
contestProblem: true,
@@ -73,8 +69,7 @@ export class AnnouncementService {
where: {
problemId,
contest: {
- id: contestId,
- groupId
+ id: contestId
}
},
orderBy: { updateTime: 'desc' }
diff --git a/apps/backend/apps/client/src/contest/contest.controller.ts b/apps/backend/apps/client/src/contest/contest.controller.ts
index b6c1e55ff4..c457b9614f 100644
--- a/apps/backend/apps/client/src/contest/contest.controller.ts
+++ b/apps/backend/apps/client/src/contest/contest.controller.ts
@@ -12,7 +12,7 @@ import {
AuthNotNeededIfOpenSpace,
UserNullWhenAuthFailedIfOpenSpace
} from '@libs/auth'
-import { GroupIDPipe, IDValidationPipe, RequiredIntPipe } from '@libs/pipe'
+import { IDValidationPipe, RequiredIntPipe } from '@libs/pipe'
import { ContestService } from './contest.service'
@Controller('contest')
@@ -37,24 +37,21 @@ export class ContestController {
@UserNullWhenAuthFailedIfOpenSpace()
async getContest(
@Req() req: AuthenticatedRequest,
- @Query('groupId', GroupIDPipe) groupId: number,
@Param('id', new RequiredIntPipe('id')) id: number
) {
- return await this.contestService.getContest(id, groupId, req.user?.id)
+ return await this.contestService.getContest(id, req.user?.id)
}
@Post(':id/participation')
async createContestRecord(
@Req() req: AuthenticatedRequest,
- @Query('groupId', GroupIDPipe) groupId: number,
@Param('id', IDValidationPipe) contestId: number,
@Query('invitationCode') invitationCode?: string
) {
return await this.contestService.createContestRecord({
contestId,
userId: req.user.id,
- invitationCode,
- groupId
+ invitationCode
})
}
@@ -62,14 +59,9 @@ export class ContestController {
@Delete(':id/participation')
async deleteContestRecord(
@Req() req: AuthenticatedRequest,
- @Query('groupId', GroupIDPipe) groupId: number,
@Param('id', IDValidationPipe) contestId: number
) {
- return await this.contestService.deleteContestRecord(
- contestId,
- req.user.id,
- groupId
- )
+ return await this.contestService.deleteContestRecord(contestId, req.user.id)
}
@Get(':id/leaderboard')
diff --git a/apps/backend/apps/client/src/contest/contest.service.spec.ts b/apps/backend/apps/client/src/contest/contest.service.spec.ts
index bd14c24e98..213ba0473f 100644
--- a/apps/backend/apps/client/src/contest/contest.service.spec.ts
+++ b/apps/backend/apps/client/src/contest/contest.service.spec.ts
@@ -1,12 +1,7 @@
import { CACHE_MANAGER } from '@nestjs/cache-manager'
import { ConfigService } from '@nestjs/config'
import { Test, type TestingModule } from '@nestjs/testing'
-import {
- Prisma,
- type Contest,
- type Group,
- type ContestRecord
-} from '@prisma/client'
+import { Prisma, type Contest, type ContestRecord } from '@prisma/client'
import { expect } from 'chai'
import * as dayjs from 'dayjs'
import {
@@ -23,14 +18,12 @@ import { ContestService, type ContestResult } from './contest.service'
const contestId = 1
const user01Id = 4
-const groupId = 1
const now = dayjs()
const contest = {
id: contestId,
createdById: 1,
- groupId,
title: 'title',
description: 'description',
penalty: 100,
@@ -44,10 +37,6 @@ const contest = {
enableCopyPaste: true,
createTime: now.add(-1, 'day').toDate(),
updateTime: now.add(-1, 'day').toDate(),
- group: {
- id: groupId,
- groupName: 'group'
- },
posterUrl: 'posterUrl',
participationTarget: 'participationTarget',
competitionMethod: 'competitionMethod',
@@ -55,14 +44,11 @@ const contest = {
problemFormat: 'problemFormat',
benefits: 'benefits',
invitationCode: '123456'
-} satisfies Contest & {
- group: Partial
-}
+} satisfies Contest
const ongoingContests = [
{
id: contest.id,
- group: contest.group,
title: contest.title,
posterUrl: contest.posterUrl,
participationTarget: contest.participationTarget,
@@ -83,7 +69,6 @@ const ongoingContests = [
const upcomingContests = [
{
id: contest.id + 6,
- group: contest.group,
title: contest.title,
posterUrl: null,
participationTarget: null,
@@ -104,7 +89,6 @@ const upcomingContests = [
const finishedContests = [
{
id: contest.id + 1,
- group: contest.group,
title: contest.title,
posterUrl: contest.posterUrl,
participationTarget: contest.participationTarget,
@@ -122,12 +106,6 @@ const finishedContests = [
}
] satisfies Partial[]
-const contests = [
- ...ongoingContests,
- ...finishedContests,
- ...upcomingContests
-] satisfies Partial[]
-
describe('ContestService', () => {
let service: ContestService
let prisma: PrismaTestService
@@ -183,7 +161,7 @@ describe('ContestService', () => {
})
it('a contest should contain following fields when userId is undefined', async () => {
- const contests = await service.getContests(groupId)
+ const contests = await service.getContests()
expect(contests.ongoing[0]).to.have.property('title')
expect(contests.ongoing[0]).to.have.property('startTime')
expect(contests.ongoing[0]).to.have.property('endTime')
@@ -223,13 +201,13 @@ describe('ContestService', () => {
describe('getContest', () => {
it('should throw error when contest does not exist', async () => {
- await expect(
- service.getContest(999, groupId, user01Id)
- ).to.be.rejectedWith(EntityNotExistException)
+ await expect(service.getContest(999, user01Id)).to.be.rejectedWith(
+ EntityNotExistException
+ )
})
it('should return contest', async () => {
- expect(await service.getContest(contestId, groupId, user01Id)).to.be.ok
+ expect(await service.getContest(contestId, user01Id)).to.be.ok
})
it('should return optional fields if they exist', async () => {
@@ -242,7 +220,7 @@ describe('ContestService', () => {
})
it('should return prev and next contest information', async () => {
- const contest = await service.getContest(contestId, groupId, user01Id)
+ const contest = await service.getContest(contestId, user01Id)
if (contest.prev) {
expect(contest.prev).to.have.property('id')
expect(contest.prev.id).to.be.lessThan(contestId)
@@ -284,9 +262,9 @@ describe('ContestService', () => {
it('should throw error when user is participated in contest again', async () => {
await expect(
service.createContestRecord({
- contestId: contestId,
+ contestId,
userId: user01Id,
- invitationCode: invitationCode
+ invitationCode
})
).to.be.rejectedWith(ConflictFoundException)
})
@@ -296,7 +274,7 @@ describe('ContestService', () => {
service.createContestRecord({
contestId: 8,
userId: user01Id,
- invitationCode: invitationCode
+ invitationCode
})
).to.be.rejectedWith(ConflictFoundException)
})
@@ -305,7 +283,7 @@ describe('ContestService', () => {
const contestRecord = await service.createContestRecord({
contestId: 2,
userId: user01Id,
- invitationCode: invitationCode
+ invitationCode
})
contestRecordId = contestRecord.id
expect(
diff --git a/apps/backend/apps/client/src/contest/contest.service.ts b/apps/backend/apps/client/src/contest/contest.service.ts
index bf8c512962..cd9783acd2 100644
--- a/apps/backend/apps/client/src/contest/contest.service.ts
+++ b/apps/backend/apps/client/src/contest/contest.service.ts
@@ -1,6 +1,5 @@
import { Injectable } from '@nestjs/common'
import { Prisma, Role, type Contest } from '@prisma/client'
-import { OPEN_SPACE_ID } from '@libs/constants'
import {
ConflictFoundException,
EntityNotExistException,
@@ -13,7 +12,6 @@ const contestSelectOption = {
title: true,
startTime: true,
endTime: true,
- group: { select: { id: true, groupName: true } },
contestRecord: {
select: {
userId: true
@@ -181,7 +179,7 @@ export class ContestService {
}
}
- async getContest(id: number, groupId = OPEN_SPACE_ID, userId?: number) {
+ async getContest(id: number, userId?: number) {
// check if the user has already registered this contest
// initial value is false
let isRegistered = false
@@ -198,7 +196,6 @@ export class ContestService {
contest = await this.prisma.contest.findUniqueOrThrow({
where: {
id,
- groupId,
isVisible: true
},
select: {
@@ -263,7 +260,6 @@ export class ContestService {
return {
where: {
id: options.compare,
- groupId,
isVisible: true
},
orderBy: {
@@ -288,20 +284,19 @@ export class ContestService {
async createContestRecord({
contestId,
userId,
- invitationCode,
- groupId = OPEN_SPACE_ID
+ invitationCode
}: {
contestId: number
userId: number
invitationCode?: string
- groupId?: number
}) {
const contest = await this.prisma.contest.findUniqueOrThrow({
- where: { id: contestId, groupId },
+ where: {
+ id: contestId
+ },
select: {
startTime: true,
endTime: true,
- groupId: true,
invitationCode: true
}
})
@@ -326,27 +321,27 @@ export class ContestService {
})
}
- async isVisible(contestId: number, groupId: number): Promise {
+ async isVisible(contestId: number): Promise {
return !!(await this.prisma.contest.count({
where: {
id: contestId,
- isVisible: true,
- groupId
+ isVisible: true
}
}))
}
- async deleteContestRecord(
- contestId: number,
- userId: number,
- groupId = OPEN_SPACE_ID
- ) {
+ async deleteContestRecord(contestId: number, userId: number) {
const [contest, contestRecord] = await Promise.all([
this.prisma.contest.findUnique({
- where: { id: contestId, groupId }
+ where: {
+ id: contestId
+ }
}),
this.prisma.contestRecord.findFirst({
- where: { userId, contestId }
+ where: {
+ userId,
+ contestId
+ }
})
])
diff --git a/apps/backend/apps/client/src/problem/problem.service.ts b/apps/backend/apps/client/src/problem/problem.service.ts
index 2b135d7d73..da1650c756 100644
--- a/apps/backend/apps/client/src/problem/problem.service.ts
+++ b/apps/backend/apps/client/src/problem/problem.service.ts
@@ -284,11 +284,7 @@ export class ContestProblemService {
take: number
groupId: number
}) {
- const contest = await this.contestService.getContest(
- contestId,
- groupId,
- userId
- )
+ const contest = await this.contestService.getContest(contestId, userId)
const now = new Date()
if (contest.isRegistered && contest.startTime! > now) {
throw new ForbiddenAccessException(
@@ -414,11 +410,7 @@ export class ContestProblemService {
userId: number
groupId: number
}) {
- const contest = await this.contestService.getContest(
- contestId,
- groupId,
- userId
- )
+ const contest = await this.contestService.getContest(contestId, userId)
const now = new Date()
if (contest.isRegistered) {
if (now < contest.startTime!) {
diff --git a/apps/backend/apps/client/src/submission/submission.controller.ts b/apps/backend/apps/client/src/submission/submission.controller.ts
index a955b56ea8..70393dde86 100644
--- a/apps/backend/apps/client/src/submission/submission.controller.ts
+++ b/apps/backend/apps/client/src/submission/submission.controller.ts
@@ -67,8 +67,7 @@ export class SubmissionController {
userIp,
userId: req.user.id,
problemId,
- contestId,
- groupId
+ contestId
})
} else if (assignmentId) {
return await this.submissionService.submitToAssignment({
diff --git a/apps/backend/apps/client/src/submission/submission.service.ts b/apps/backend/apps/client/src/submission/submission.service.ts
index 2abd8dc7ca..178633fcbe 100644
--- a/apps/backend/apps/client/src/submission/submission.service.ts
+++ b/apps/backend/apps/client/src/submission/submission.service.ts
@@ -143,15 +143,13 @@ export class SubmissionService {
userIp,
userId,
problemId,
- contestId,
- groupId = OPEN_SPACE_ID
+ contestId
}: {
submissionDto: CreateSubmissionDto
userIp: string
userId: number
problemId: number
contestId: number
- groupId: number
}) {
const now = new Date()
@@ -159,7 +157,6 @@ export class SubmissionService {
const contest = await this.prisma.contest.findFirst({
where: {
id: contestId,
- groupId,
startTime: {
lte: now
},
@@ -184,7 +181,6 @@ export class SubmissionService {
select: {
contest: {
select: {
- groupId: true,
startTime: true,
endTime: true
}
@@ -194,9 +190,7 @@ export class SubmissionService {
if (!contestRecord) {
throw new EntityNotExistException('ContestRecord')
}
- if (contestRecord.contest.groupId !== groupId) {
- throw new EntityNotExistException('Contest')
- } else if (
+ if (
contestRecord.contest.startTime > now ||
contestRecord.contest.endTime <= now
) {
@@ -957,7 +951,6 @@ export class SubmissionService {
}) {
const now = new Date()
let contest: {
- groupId: number
startTime: Date
endTime: Date
isJudgeResultVisible: boolean
@@ -982,7 +975,6 @@ export class SubmissionService {
select: {
contest: {
select: {
- groupId: true,
startTime: true,
endTime: true,
isJudgeResultVisible: true
@@ -993,9 +985,6 @@ export class SubmissionService {
if (!contestRecord) {
throw new EntityNotExistException('ContestRecord')
}
- if (contestRecord.contest.groupId !== groupId) {
- throw new EntityNotExistException('Contest')
- }
contest = contestRecord.contest
isJudgeResultVisible = contest.isJudgeResultVisible
} else if (assignmentId) {
diff --git a/apps/backend/apps/client/src/submission/test/submission.service.spec.ts b/apps/backend/apps/client/src/submission/test/submission.service.spec.ts
index 2a5e1d3158..591438724a 100644
--- a/apps/backend/apps/client/src/submission/test/submission.service.spec.ts
+++ b/apps/backend/apps/client/src/submission/test/submission.service.spec.ts
@@ -84,7 +84,6 @@ const WORKBOOK_ID = 1
const mockContest: Contest = {
id: CONTEST_ID,
createdById: 1,
- groupId: 1,
title: 'SKKU Coding Platform 모의대회',
description: 'test',
penalty: 20,
@@ -246,8 +245,7 @@ describe('SubmissionService', () => {
userIp: USERIP,
userId: submissions[0].userId,
problemId: problems[0].id,
- contestId: CONTEST_ID,
- groupId: problems[0].groupId
+ contestId: CONTEST_ID
})
expect(createSpy.calledOnce).to.be.true
})
@@ -262,8 +260,7 @@ describe('SubmissionService', () => {
userIp: USERIP,
userId: submissions[0].userId,
problemId: problems[0].id,
- contestId: CONTEST_ID,
- groupId: problems[0].groupId
+ contestId: CONTEST_ID
})
).to.be.rejectedWith(EntityNotExistException)
expect(createSpy.called).to.be.false
diff --git a/apps/backend/prisma/migrations/20250130102040_delete_group_from_contest/migration.sql b/apps/backend/prisma/migrations/20250130102040_delete_group_from_contest/migration.sql
new file mode 100644
index 0000000000..a80ac62fe1
--- /dev/null
+++ b/apps/backend/prisma/migrations/20250130102040_delete_group_from_contest/migration.sql
@@ -0,0 +1,11 @@
+/*
+ Warnings:
+
+ - You are about to drop the column `group_id` on the `contest` table. All the data in the column will be lost.
+
+*/
+-- DropForeignKey
+ALTER TABLE "contest" DROP CONSTRAINT "contest_group_id_fkey";
+
+-- AlterTable
+ALTER TABLE "contest" DROP COLUMN "group_id";
diff --git a/apps/backend/prisma/schema.prisma b/apps/backend/prisma/schema.prisma
index 96814a6c2b..e8c59263a9 100644
--- a/apps/backend/prisma/schema.prisma
+++ b/apps/backend/prisma/schema.prisma
@@ -122,7 +122,6 @@ model Group {
problem Problem[]
assignment Assignment[]
workbook Workbook[]
- contest Contest[]
@@map("group")
}
@@ -327,11 +326,9 @@ model AssignmentRecord {
}
model Contest {
- id Int @id @default(autoincrement())
- createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull)
- createdById Int? @map("created_by_id")
- group Group @relation(fields: [groupId], references: [id])
- groupId Int @map("group_id")
+ id Int @id @default(autoincrement())
+ createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull)
+ createdById Int? @map("created_by_id")
title String
description String
// 대회의 페널티 (0 ≤ penalty ≤ 100),
diff --git a/apps/backend/prisma/seed.ts b/apps/backend/prisma/seed.ts
index 09b1219c02..d277373470 100644
--- a/apps/backend/prisma/seed.ts
+++ b/apps/backend/prisma/seed.ts
@@ -958,7 +958,6 @@ const createContests = async () => {
title: string
description: string
createdById: number
- groupId: number
posterUrl: string | null
participationTarget: string | null
competitionMethod: string | null
@@ -1008,7 +1007,6 @@ const createContests = async () => {
아니하고는 처벌·보안처분 또는 강제노역을 받지 아니한다.
`,
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: `https://skkuding.dev/open-graph.png`,
participationTarget: '성균관대 재학생이라면 누구나',
competitionMethod: '온라인으로 진행',
@@ -1028,7 +1026,6 @@ const createContests = async () => {
title: '24년도 소프트웨어학과 신입생 입학 테스트1',
description: '이 대회는 현재 진행 중입니다 !
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '성균관대학교 24학번 신입생',
competitionMethod: '강의실에서 오프라인으로 진행',
@@ -1048,7 +1045,6 @@ const createContests = async () => {
title: '24년도 소프트웨어학과 신입생 입학 테스트2',
description: '이 대회는 현재 진행 중입니다 !
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: `https://skkuding.dev/open-graph.png`,
participationTarget: '성균관대학교 24학번 신입생',
competitionMethod: '강의실에서 오프라인으로 진행',
@@ -1068,7 +1064,6 @@ const createContests = async () => {
title: '24년도 소프트웨어학과 신입생 입학 테스트3',
description: '이 대회는 현재 진행 중입니다 !
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: `https://skkuding.dev/open-graph.png`,
participationTarget: '성균관대학교 24학번 신입생',
competitionMethod: '강의실에서 오프라인으로 진행',
@@ -1088,7 +1083,6 @@ const createContests = async () => {
title: '24년도 아늑배 스파게티 코드 만들기 대회',
description: '이 대회는 현재 진행 중입니다 ! (private group)
',
createdById: superAdminUser.id,
- groupId: privateGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '삼성학술정보관 지하1층에서 오프라인 진행',
@@ -1109,7 +1103,6 @@ const createContests = async () => {
title: 'Long Time Ago Assignment',
description: '이 대회는 오래 전에 끝났어요
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공생',
competitionMethod: '온라인 진행',
@@ -1129,7 +1122,6 @@ const createContests = async () => {
title: '23년도 소프트웨어학과 신입생 입학 테스트',
description: '이 대회는 오래 전에 끝났어요
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 23학번',
competitionMethod: '온라인 진행',
@@ -1149,7 +1141,6 @@ const createContests = async () => {
title: '소프트의 아침',
description: '이 대회는 오래 전에 끝났어요
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행',
@@ -1169,7 +1160,6 @@ const createContests = async () => {
title: '소프트의 낮',
description: '이 대회는 오래 전에 끝났어요
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행',
@@ -1189,7 +1179,6 @@ const createContests = async () => {
title: '소프트의 밤',
description: '이 대회는 오래 전에 끝났어요
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행',
@@ -1209,7 +1198,6 @@ const createContests = async () => {
title: '2023 SKKU 프로그래밍 대회',
description: '이 대회는 오래 전에 끝났어요
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행',
@@ -1229,7 +1217,6 @@ const createContests = async () => {
title: '소프트의 오전',
description: '이 대회는 오래 전에 끝났어요
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행',
@@ -1249,7 +1236,6 @@ const createContests = async () => {
title: '소프트의 오후',
description: '이 대회는 오래 전에 끝났어요
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행',
@@ -1269,7 +1255,6 @@ const createContests = async () => {
title: '23년도 아늑배 스파게티 코드 만들기 대회',
description: '이 대회는 오래 전에 끝났어요 (private group)
',
createdById: superAdminUser.id,
- groupId: privateGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행',
@@ -1290,7 +1275,6 @@ const createContests = async () => {
title: 'Future Assignment',
description: '이 대회는 언젠가 열리겠죠...?
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행 예정...?',
@@ -1310,7 +1294,6 @@ const createContests = async () => {
title: '2024 SKKU 프로그래밍 대회',
description: '이 대회는 언젠가 열리겠죠...?
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행 예정...?',
@@ -1331,7 +1314,6 @@ const createContests = async () => {
description:
'이 대회는 언젠가 열리겠죠...? isVisible이 false인 assignment입니다
',
createdById: superAdminUser.id,
- groupId: publicGroup.id,
posterUrl: `https://skkuding.dev/open-graph.png`,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행 예정...?',
@@ -1351,7 +1333,6 @@ const createContests = async () => {
title: '25년도 아늑배 스파게티 코드 만들기 대회',
description: '이 대회는 언젠가 열리겠죠...? (private group)
',
createdById: superAdminUser.id,
- groupId: privateGroup.id,
posterUrl: null,
participationTarget: '소프트웨어학과 원전공/복수전공',
competitionMethod: '온라인 진행 예정...?',
@@ -2396,39 +2377,58 @@ const createAssignmentRecords = async () => {
const createContestRecords = async () => {
const contestRecords: ContestRecord[] = []
- // group 1 users
- const group1Users = await prisma.userGroup.findMany({
- where: {
- groupId: 1
- }
- })
- for (const user of group1Users) {
- const contestRecord = await prisma.contestRecord.create({
- data: {
- userId: user.userId,
+ // all users
+ const users = await prisma.user.findMany()
+ for (const user of users) {
+ const existingRecord = await prisma.contestRecord.findFirst({
+ where: {
contestId: 1,
- acceptedProblemNum: 0,
- totalPenalty: 0
+ userId: user.id
}
})
- contestRecords.push(contestRecord)
- }
-
- // upcoming contest에 참가한 User 1의 contest register를 un-register하는 기능과,
- // registered upcoming, ongoing, finished contest를 조회하는 기능을 확인하기 위함
- const user01Id = 4
- for (let contestId = 3; contestId <= contests.length; contestId += 2) {
- contestRecords.push(
- await prisma.contestRecord.create({
+ if (!existingRecord) {
+ const contestRecord = await prisma.contestRecord.create({
data: {
- userId: user01Id,
- contestId,
+ userId: user.id,
+ contestId: 1,
acceptedProblemNum: 0,
- score: 0,
totalPenalty: 0
}
})
- )
+ contestRecords.push(contestRecord)
+ }
+ }
+
+ // upcoming contest에 참가한 User 1의 contest register를 un-register하는 기능과,
+ // registered upcoming, ongoing, finished contest를 조회하는 기능을 확인하기 위함
+ const contests = await prisma.contest.findMany({
+ select: {
+ id: true
+ }
+ })
+ const user01Id = 4
+ for (let i = 0; i < contests.length; i += 2) {
+ const contestId = contests[i].id
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const existingRecord = await prisma.contestRecord.findFirst({
+ where: {
+ contestId: contestId,
+ userId: user01Id
+ }
+ })
+ if (!existingRecord) {
+ contestRecords.push(
+ await prisma.contestRecord.create({
+ data: {
+ userId: user01Id,
+ contestId,
+ acceptedProblemNum: 0,
+ score: 0,
+ totalPenalty: 0
+ }
+ })
+ )
+ }
}
return contestRecords