Skip to content

Commit

Permalink
[PR][#54] 스터디 공지 CRUD (#65)
Browse files Browse the repository at this point in the history
* feat: Add notice module

* feat: Add get notices by study id api
  • Loading branch information
selgyun authored Aug 27, 2023
1 parent 1c7078c commit dcbe3be
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 3 deletions.
7 changes: 7 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { InternalUserModule } from './internal-user/internal-user.module';
import { QuestionModule } from './question/question.module';
import { ApplyFormModule } from './apply-form/apply-form.module';
import { AnswerModule } from './answer/answer.module';
import { NoticeModule } from './notice/notice.module';

@Module({
imports: [
Expand All @@ -23,6 +24,7 @@ import { AnswerModule } from './answer/answer.module';
QuestionModule,
ApplyFormModule,
AnswerModule,
NoticeModule,
RouterModule.register([
{
path: 'internal/api/v1',
Expand Down Expand Up @@ -58,8 +60,13 @@ import { AnswerModule } from './answer/answer.module';
path: 'api/v1/answer',
module: AnswerModule,
},
{
path: 'api/v1/notice',
module: NoticeModule,
},
]),
AnswerModule,
NoticeModule,
],
controllers: [AppController],
providers: [AppService, Logger],
Expand Down
2 changes: 2 additions & 0 deletions src/common/studium-exception.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export class StudiumException {
public static readonly test = new StudiumException('TEST', 'something bad happened');
public static readonly test2 = new StudiumException('TEST', 'something bad');
public static readonly dataNotFound = new StudiumException('Data-Not-Found', 'requested data is missing');
public static readonly idFormatError = new StudiumException('ID-is-NaN', 'invalid ID format: not a number');

public readonly code: string;
public readonly message: string;
Expand Down
13 changes: 13 additions & 0 deletions src/database/migrations/20230822074102_add_notice/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- CreateTable
CREATE TABLE "Notice" (
"id" SERIAL NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"content" TEXT NOT NULL,
"studyId" INTEGER NOT NULL,

CONSTRAINT "Notice_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "Notice" ADD CONSTRAINT "Notice_studyId_fkey" FOREIGN KEY ("studyId") REFERENCES "Study"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
37 changes: 37 additions & 0 deletions src/notice/dto/create-notice-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsDate, IsString } from 'class-validator';
import { Notice } from '@prisma/client';

export class CreateNoticeResponseDto {
@ApiProperty()
@IsNumber()
readonly id: number;

@ApiProperty()
@IsDate()
readonly createdAt: Date

@ApiProperty()
@IsDate()
readonly updatedAt: Date

@ApiProperty()
@IsString()
readonly content: String

@ApiProperty()
@IsNumber()
readonly studyId: number;

constructor(id: number, createdAt: Date, updatedAt: Date, content: string, studyId: number) {
this.id = id;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.content = content;
this.studyId = studyId;
}

static fromNotice(notice: Notice) {
return new CreateNoticeResponseDto(notice.id, notice.createdAt, notice.updatedAt, notice.content, notice.studyId);
}
}
12 changes: 12 additions & 0 deletions src/notice/dto/create-notice.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsString } from 'class-validator';

export class CreateNoticeDto {
@ApiProperty()
@IsString()
readonly content: string;

@ApiProperty()
@IsNumber()
readonly studyId: number;
}
37 changes: 37 additions & 0 deletions src/notice/dto/get-notice-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsDate, IsString } from 'class-validator';
import { Notice } from '@prisma/client';

export class GetNoticeResponseDto {
@ApiProperty()
@IsNumber()
readonly id: number;

@ApiProperty()
@IsDate()
readonly createdAt: Date

@ApiProperty()
@IsDate()
readonly updatedAt: Date

@ApiProperty()
@IsString()
readonly content: String

@ApiProperty()
@IsNumber()
readonly studyId: number;

constructor(id: number, createdAt: Date, updatedAt: Date, content: string, studyId: number) {
this.id = id;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.content = content;
this.studyId = studyId;
}

static fromNotice(notice: Notice) {
return new GetNoticeResponseDto(notice.id, notice.createdAt, notice.updatedAt, notice.content, notice.studyId);
}
}
24 changes: 24 additions & 0 deletions src/notice/dto/update-notice-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Notice } from "@prisma/client";
import { CreateNoticeResponseDto } from "./create-notice-response.dto";

export class UpdateNoticeResponseDto extends CreateNoticeResponseDto{
constructor(
id: number,
createdAt: Date,
updatedAt: Date,
content: string,
studyId: number
) {
super(id, createdAt, updatedAt, content, studyId);
}

static fromNotice(notice: Notice) {
return new UpdateNoticeResponseDto(
notice.id,
notice.createdAt,
notice.updatedAt,
notice.content,
notice.studyId
);
}
}
4 changes: 4 additions & 0 deletions src/notice/dto/update-notice.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/swagger';
import { CreateNoticeDto } from './create-notice.dto';

export class UpdateNoticeDto extends PartialType(CreateNoticeDto) {}
29 changes: 29 additions & 0 deletions src/notice/notice.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Controller, Get, Post, Patch, Delete, Param, Body } from '@nestjs/common';
import { NoticeService } from './notice.service';
import { CreateNoticeDto } from './dto/create-notice.dto';
import { UpdateNoticeDto } from './dto/update-notice.dto';

@Controller()
export class NoticeController {
constructor(private readonly noticeService: NoticeService) {}

@Get()
findAll() {
return this.noticeService.findAll();
}

@Get(':id')
findOne(@Param('id') id: string) {
return this.noticeService.findOne(+id);
}

@Post()
create(@Body() createNoticeDto: CreateNoticeDto) {
return this.noticeService.create(createNoticeDto);
}

@Patch(':id')
update(@Param('id') id: string, @Body() updateNoticeDto: UpdateNoticeDto) {
return this.noticeService.update(+id, updateNoticeDto);
}
}
11 changes: 11 additions & 0 deletions src/notice/notice.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { NoticeController } from './notice.controller';
import { NoticeService } from './notice.service';
import { PrismaModule } from 'src/database/prisma.module';

@Module({
controllers: [NoticeController],
providers: [NoticeService],
imports : [PrismaModule],
})
export class NoticeModule {}
67 changes: 67 additions & 0 deletions src/notice/notice.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { BadRequestException, Injectable, InternalServerErrorException } from '@nestjs/common';
import { PrismaService } from 'src/database/prisma.service';
import { GetNoticeResponseDto } from './dto/get-notice-response.dto';
import { CreateNoticeResponseDto } from './dto/create-notice-response.dto';
import { CreateNoticeDto } from './dto/create-notice.dto';
import { UpdateNoticeDto } from './dto/update-notice.dto';
import { UpdateNoticeResponseDto } from './dto/update-notice-response.dto';
import { StudiumException } from 'src/common/studium-exception';

@Injectable()
export class NoticeService {
constructor(private prisma: PrismaService) {}

async findAll(): Promise<GetNoticeResponseDto[]> {
const notices = await this.prisma.notice.findMany();
return notices.map((notice) => GetNoticeResponseDto.fromNotice(notice));
}

async findOne(id: number): Promise<GetNoticeResponseDto> {
if (isNaN(id)) {
throw new BadRequestException(StudiumException.idFormatError);
}
const notice = await this.prisma.notice.findUnique({ where: { id } })

if (!notice) {
throw new InternalServerErrorException(StudiumException.dataNotFound);
}

return GetNoticeResponseDto.fromNotice(notice);
}

async create(createNoticeDto: CreateNoticeDto): Promise<CreateNoticeResponseDto> {
const {studyId, ...data} = createNoticeDto;
const notice = await this.prisma.notice.create({
data: {
study: { connect: {id: studyId } },
...data
},
include: {
study: true,
}
});
return CreateNoticeResponseDto.fromNotice(notice);
}

async update(id: number, updateNoticeDto: UpdateNoticeDto): Promise<UpdateNoticeResponseDto> {
const { studyId, ...data } = updateNoticeDto;

const notice = await this.prisma.notice.update({
where: { id },
data: {
...data
}
});

return UpdateNoticeResponseDto.fromNotice(notice);
}

async remove(id: number) {
const noticeToDelete = await this.prisma.notice.findUnique({
where: { id },
include: { study: true },
});

return await this.prisma.notice.delete({ where: { id: noticeToDelete.id } });
}
}
6 changes: 6 additions & 0 deletions src/study/study.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { UpdateStudyDto } from './dto/update-study.dto';
import { CreateStudyResponseDto } from './dto/create-study-response.dto';
import { GetStudyResponseDto } from './dto/get-study-response.dto';
import { UpdateStudyResponseDto } from './dto/update-study-response.dto';
import { GetNoticeResponseDto } from 'src/notice/dto/get-notice-response.dto';

@Controller()
export class StudyController {
Expand All @@ -30,6 +31,11 @@ export class StudyController {
return this.studyService.findOne(+id);
}

@Get(':id/notices')
findNotices(@Param('id') id: string): Promise<GetNoticeResponseDto[]> {
return this.studyService.findNotices(+id);
}

@Patch(':id')
update(@Param('id') id: string, @Body() updateStudyDto: UpdateStudyDto): Promise<UpdateStudyResponseDto> {
return this.studyService.update(+id, updateStudyDto);
Expand Down
18 changes: 17 additions & 1 deletion src/study/study.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Injectable } from '@nestjs/common';
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { CreateStudyDto } from './dto/create-study.dto';
import { UpdateStudyDto } from './dto/update-study.dto';
import { PrismaService } from 'src/database/prisma.service';
import { CreateStudyResponseDto } from './dto/create-study-response.dto';
import { GetStudyResponseDto } from './dto/get-study-response.dto';
import { UpdateStudyResponseDto } from './dto/update-study-response.dto';
import { GetNoticeResponseDto } from 'src/notice/dto/get-notice-response.dto';
import { StudiumException } from 'src/common/studium-exception';

@Injectable()
export class StudyService {
Expand Down Expand Up @@ -57,6 +59,11 @@ export class StudyService {
questions: true,
},
});

if (!study) {
throw new InternalServerErrorException(StudiumException.dataNotFound);
}

return GetStudyResponseDto.fromStudy(study);
}

Expand Down Expand Up @@ -157,4 +164,13 @@ export class StudyService {
});
return UpdateStudyResponseDto.fromStudy(study);
}

async findNotices(id: number): Promise<GetNoticeResponseDto[]> {
const study = await this.findOne(id);
const notices = await this.prisma.notice.findMany({
where: { studyId : study.id }
});

return notices.map((notice) => GetNoticeResponseDto.fromNotice(notice));
}
}
5 changes: 3 additions & 2 deletions src/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { UpdateUserDto } from './dto/update-user.dto';
import { GetUserResponseDto } from './dto/get-user-response.dto';
import { CreateUserResponseDto } from './dto/create-user-response.dto';
import { UpdateUserResponseDto } from './dto/update-user-response.dto';
import { StudiumException } from 'src/common/studium-exception';

@Injectable()
export class UserService {
Expand All @@ -18,15 +19,15 @@ export class UserService {

async findOne(userId: number): Promise<GetUserResponseDto>{
if (isNaN(userId)) {
throw new BadRequestException('Bad request for find unique user.');
throw new BadRequestException(StudiumException.idFormatError);
}

const user: User = await this.prisma.user.findUnique({
where: { id: userId },
});

if (!user) {
throw new InternalServerErrorException(`User with ID: ${userId} not Found.`);
throw new InternalServerErrorException(StudiumException.dataNotFound);
}

return GetUserResponseDto.fromUser(user);
Expand Down

0 comments on commit dcbe3be

Please sign in to comment.