-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(be): implement admin announcement module #1270
Open
Lee-won-hyeok
wants to merge
33
commits into
main
Choose a base branch
from
1047-implement-admin-announcement-module
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
2897d81
feat(be): implement admin annoouncement create and read
Lee-won-hyeok 10b70ef
feat(be): implement admin annoouncement create and read
Lee-won-hyeok a1e231f
feat(be): implement admin annoouncement create and read
Lee-won-hyeok 77fd160
Merge branch 'main' into 1047-implement-admin-announcement-module
Lee-won-hyeok 4d2cc7b
feat: implement admin announcement update and delete
Lee-won-hyeok 485b8a2
feat: add test code and additional error handler
Lee-won-hyeok 2b2cbb6
docs: add bruno announcement requests
Lee-won-hyeok 997c8a6
Merge branch 'main' into 1047-implement-admin-announcement-module
Lee-won-hyeok 06be8aa
feat: change business exception handling
Lee-won-hyeok 1f7fb66
Merge branch 'main' into 1047-implement-admin-announcement-module
Lee-won-hyeok 7cd01ee
chore: change business exception handling
Lee-won-hyeok 6c1cf38
docs: bruno collection docs
Lee-won-hyeok 1ed30ac
chore: change prisma exception handler
Lee-won-hyeok 966f437
fix: update test code
Lee-won-hyeok 8f81940
feat: match service and resolver function name
Lee-won-hyeok 95a6e4c
chore: fix test code
Lee-won-hyeok 63ad162
chore: fix ts issue
Lee-won-hyeok eb22bb7
Merge branch 'main' into 1047-implement-admin-announcement-module
Lee-won-hyeok 2c4a950
feat: fix create & update logic
Lee-won-hyeok 710f50c
docs: submission bruno docs & rename API
Lee-won-hyeok 92f0c01
Merge branch 'main' into 1047-implement-admin-announcement-module
Lee-won-hyeok dacd6cf
feat: update announcement test code
Lee-won-hyeok 961ac1d
docs: update bruno
Lee-won-hyeok c510531
Merge branch 'main' into 1047-implement-admin-announcement-module
Lee-won-hyeok 79a97dd
docs: bruno assert
Lee-won-hyeok 0c96747
docs: write bruno docs
Lee-won-hyeok 65076cf
Merge branch 'main' into 1047-implement-admin-announcement-module
Lee-won-hyeok e9d485f
chore: remove unused dto
Lee-won-hyeok 826f129
docs: update bruno docs
Lee-won-hyeok b96cc1d
Merge branch 'main' into 1047-implement-admin-announcement-module
Lee-won-hyeok 2aadb11
Merge branch 'main' into 1047-implement-admin-announcement-module
Lee-won-hyeok 81c2f17
docs: update bruno docs
Lee-won-hyeok 5d5a0a7
Merge branch '1047-implement-admin-announcement-module' of https://gi…
Lee-won-hyeok File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
apps/backend/apps/admin/src/announcement/announcement.resolver.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { Test, type TestingModule } from '@nestjs/testing' | ||
import { expect } from 'chai' | ||
import { AnnouncementResolver } from './announcement.resolver' | ||
import { AnnouncementService } from './announcement.service' | ||
|
||
describe('AnnouncementResolver', () => { | ||
let resolver: AnnouncementResolver | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
AnnouncementResolver, | ||
{ provide: AnnouncementService, useValue: {} } | ||
] | ||
}).compile() | ||
|
||
resolver = module.get<AnnouncementResolver>(AnnouncementResolver) | ||
}) | ||
|
||
it('shoulld be defined', () => { | ||
expect(resolver).to.be.ok | ||
}) | ||
}) |
89 changes: 68 additions & 21 deletions
89
apps/backend/apps/admin/src/announcement/announcement.resolver.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,91 @@ | ||
import { InternalServerErrorException, Logger } from '@nestjs/common' | ||
import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql' | ||
import { Announcement } from '@generated' | ||
import { | ||
DuplicateFoundException, | ||
EntityNotExistException | ||
} from '@libs/exception' | ||
import { Announcement } from '@admin/@generated' | ||
import { AnnouncementService } from './announcement.service' | ||
import { CreateAnnouncementInput } from './dto/create-announcement.input' | ||
import { UpdateAnnouncementInput } from './dto/update-announcement.input' | ||
import { AnnouncementInput } from './dto/announcement.input' | ||
|
||
@Resolver(() => Announcement) | ||
export class AnnouncementResolver { | ||
private readonly logger = new Logger(AnnouncementResolver.name) | ||
constructor(private readonly announcementService: AnnouncementService) {} | ||
|
||
@Mutation(() => Announcement) | ||
createAnnouncement( | ||
async createAnnouncement( | ||
@Args('createAnnouncementInput') | ||
createAnnouncementInput: CreateAnnouncementInput | ||
announcementInput: AnnouncementInput | ||
) { | ||
return this.announcementService.create(createAnnouncementInput) | ||
try { | ||
return await this.announcementService.createAnnouncement( | ||
announcementInput | ||
) | ||
} catch (error) { | ||
if (error instanceof DuplicateFoundException || EntityNotExistException) { | ||
throw error.convert2HTTPException() | ||
} | ||
this.logger.error(error) | ||
throw new InternalServerErrorException() | ||
} | ||
} | ||
|
||
@Query(() => [Announcement], { name: 'announcement' }) | ||
findAll() { | ||
return this.announcementService.findAll() | ||
@Query(() => [Announcement], { name: 'getAnnouncements' }) | ||
async getAnnouncements( | ||
@Args('contestId', { type: () => Int }) contestId: number, | ||
@Args('problemId', { type: () => Int, nullable: true }) problemId?: number | ||
) { | ||
try { | ||
return await this.announcementService.getAnnouncements( | ||
contestId, | ||
problemId | ||
) | ||
} catch (error) { | ||
this.logger.error(error) | ||
throw new InternalServerErrorException() | ||
} | ||
} | ||
|
||
@Query(() => Announcement, { name: 'announcement' }) | ||
findOne(@Args('id', { type: () => Int }) id: number) { | ||
return this.announcementService.findOne(id) | ||
@Query(() => Announcement, { name: 'getAnnouncementById' }) | ||
async getAnnouncementById(@Args('id', { type: () => Int }) id: number) { | ||
try { | ||
return await this.announcementService.getAnnouncementById(id) | ||
} catch (error) { | ||
if (error instanceof EntityNotExistException) { | ||
throw error.convert2HTTPException() | ||
} | ||
this.logger.error(error) | ||
throw new InternalServerErrorException() | ||
} | ||
} | ||
|
||
@Mutation(() => Announcement) | ||
updateAnnouncement( | ||
@Args('updateAnnouncementInput') | ||
updateAnnouncementInput: UpdateAnnouncementInput | ||
async updateAnnouncement( | ||
@Args('id', { type: () => Int }) id: number, | ||
@Args('content', { type: () => String }) content: string | ||
) { | ||
return this.announcementService.update( | ||
updateAnnouncementInput.id, | ||
updateAnnouncementInput | ||
) | ||
try { | ||
return await this.announcementService.updateAnnouncement(id, content) | ||
} catch (error) { | ||
if (error instanceof EntityNotExistException) { | ||
throw error.convert2HTTPException() | ||
} | ||
this.logger.error(error) | ||
throw new InternalServerErrorException() | ||
} | ||
} | ||
|
||
@Mutation(() => Announcement) | ||
removeAnnouncement(@Args('id', { type: () => Int }) id: number) { | ||
return this.announcementService.remove(id) | ||
async removeAnnouncement(@Args('id', { type: () => Int }) id: number) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 진짜진짜 사소한건데... CRUD니까 remove대신 delete 어떨까요 ㅎㅎ.. |
||
try { | ||
return await this.announcementService.removeAnnouncement(id) | ||
} catch (error) { | ||
if (error instanceof EntityNotExistException) { | ||
throw error.convert2HTTPException() | ||
} | ||
this.logger.error(error) | ||
throw new InternalServerErrorException() | ||
} | ||
} | ||
} |
147 changes: 147 additions & 0 deletions
147
apps/backend/apps/admin/src/announcement/announcement.service.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import { Test, type TestingModule } from '@nestjs/testing' | ||
import { faker } from '@faker-js/faker' | ||
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library' | ||
import { expect } from 'chai' | ||
import { stub } from 'sinon' | ||
import { | ||
DuplicateFoundException, | ||
EntityNotExistException | ||
} from '@libs/exception' | ||
import { PrismaService } from '@libs/prisma' | ||
import { AnnouncementService } from './announcement.service' | ||
|
||
const problemId = faker.number.int() | ||
const contestId = faker.number.int() | ||
const id = faker.number.int() | ||
const announcementInput = { | ||
problemId, | ||
contestId, | ||
content: faker.string.sample() | ||
} | ||
|
||
const announcement = { | ||
...announcementInput, | ||
id | ||
} | ||
|
||
const contestProblem = { | ||
problemId | ||
} | ||
|
||
const db = { | ||
announcement: { | ||
findFirst: stub(), | ||
findFirstOrThrow: stub(), | ||
findMany: stub().resolves([announcement]), | ||
update: stub(), | ||
delete: stub(), | ||
create: stub().resolves(announcement) | ||
}, | ||
contestProblem: { | ||
findFirst: stub(), | ||
findFirstOrThrow: stub() | ||
} | ||
} | ||
|
||
const PrismaErrorInstance = new PrismaClientKnownRequestError('error', { | ||
code: '4xx', | ||
clientVersion: 'x.x.x' | ||
}) | ||
|
||
PrismaErrorInstance.name = 'NotFoundError' | ||
|
||
describe('AnnouncementService', () => { | ||
let service: AnnouncementService | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [AnnouncementService, { provide: PrismaService, useValue: db }] | ||
}).compile() | ||
|
||
service = module.get<AnnouncementService>(AnnouncementService) | ||
}) | ||
|
||
it('should be defined', () => { | ||
expect(service).to.be.ok | ||
}) | ||
|
||
describe('createAnnouncement', () => { | ||
it('should return created announcement', async () => { | ||
db.announcement.findFirst.resolves(null) | ||
db.contestProblem.findFirstOrThrow.resolves(contestProblem) | ||
|
||
const res = await service.createAnnouncement(announcementInput) | ||
expect(res).to.deep.equal(announcement) | ||
}) | ||
|
||
it('should throw error when given announcement already exists', async () => { | ||
db.announcement.findFirst.resolves(announcement) | ||
|
||
await expect( | ||
service.createAnnouncement(announcementInput) | ||
).to.be.rejectedWith(DuplicateFoundException) | ||
}) | ||
|
||
it('should throw error when given problemId is invalid', async () => { | ||
db.announcement.findFirst.resolves(null) | ||
db.contestProblem.findFirstOrThrow.rejects(PrismaErrorInstance) | ||
|
||
await expect( | ||
service.createAnnouncement(announcementInput) | ||
).to.be.rejectedWith(EntityNotExistException) | ||
}) | ||
}) | ||
|
||
describe('getAnnouncements', () => { | ||
it('should return all announcements', async () => { | ||
db.announcement.findMany() | ||
const res = await service.getAnnouncements(problemId) | ||
expect(res).to.deep.equal([announcement]) | ||
}) | ||
}) | ||
|
||
describe('getAnnouncementById', () => { | ||
it('should throw error when given id is invalid', async () => { | ||
db.announcement.findFirstOrThrow.rejects(PrismaErrorInstance) | ||
await expect(service.getAnnouncementById(id)).to.be.rejectedWith( | ||
EntityNotExistException | ||
) | ||
}) | ||
it('should return an announcement', async () => { | ||
db.announcement.findFirstOrThrow.resolves(announcement) | ||
const res = await service.getAnnouncementById(id) | ||
expect(res).to.deep.equal(announcement) | ||
}) | ||
}) | ||
|
||
describe('updateAnnouncement', () => { | ||
it('should return updated announcement', async () => { | ||
db.announcement.update.resolves(announcement) | ||
const res = await service.updateAnnouncement( | ||
id, | ||
announcementInput.content | ||
) | ||
expect(res).to.deep.equal(announcement) | ||
}) | ||
it('should throw error when given id is invalid', async () => { | ||
db.announcement.update.rejects(PrismaErrorInstance) | ||
await expect( | ||
service.updateAnnouncement(id, announcementInput.content) | ||
).to.be.rejectedWith(EntityNotExistException) | ||
}) | ||
}) | ||
|
||
describe('removeAnnouncement', () => { | ||
it('should return deleted announcement', async () => { | ||
db.announcement.delete.resolves(announcement) | ||
const res = await service.removeAnnouncement(id) | ||
expect(res).to.deep.equal(announcement) | ||
}) | ||
it('should throw error when given id is invalid', async () => { | ||
db.announcement.delete.rejects(PrismaErrorInstance) | ||
await expect(service.removeAnnouncement(id)).to.be.rejectedWith( | ||
EntityNotExistException | ||
) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 Query name을 따로 'getAnnouncements'로 지정하지 않아도 밑에 메소드 이름으로 적용될거에요!