diff --git a/apps/backend/src/app.controller.ts b/apps/backend/src/app.controller.ts index b0b7c15..53ee2a8 100644 --- a/apps/backend/src/app.controller.ts +++ b/apps/backend/src/app.controller.ts @@ -14,6 +14,8 @@ import { IssueType } from './issues/entities/issue-type.enum'; import { CreateIssueDto } from './issues/dto/create-issue.dto'; import { CreateIssueCommentDto } from './issues/dto/create-issue-comment.dto'; import { SolutionsService } from './solutions/solutions.service'; +import { CreateSolutionDto } from './solutions/dto/create-solution.dto'; +import { CreateSolutionCommentDto } from './solutions/dto/create-solution-comment.dto'; @Controller() export class AppController { @@ -39,20 +41,27 @@ export class AppController { @Get('/issues/:id') async getIssue(@Param('id', ParseUUIDPipe) id: string) { const data = await this.issues.findOne(id).then((i) => i.toDto()); - const comments = await this.issues.getCommentsForIssue(id); + const comments = await this.issues + .getCommentsForIssue(id) + .then((c) => c.map((c) => c.toDto())); const solution = - (await this.solutions.getForIssue(id))?.toList() ?? undefined; + (await this.solutions.getForIssue(id).catch(() => undefined))?.toList() ?? + undefined; return { data, comments, solution }; } @Get('/issues/:id/solution') async getSolution(@Param('id', ParseUUIDPipe) id: string) { - return this.solutions.getForIssue(id).then((i) => i.toDto()); + const data = await this.solutions.getForIssue(id).then((i) => i.toDto()); + const comments = await this.solutions + .getComments(id) + .then((c) => c.map((c) => c.toDto())); + return { data, comments }; } @Post('/issues') createIssue(@Body() dto: CreateIssueDto) { - return this.issues.create(dto); + return this.issues.create(dto).then((i) => i.toDto()); } @Post('/issues/:id/comments') @@ -63,6 +72,22 @@ export class AppController { return this.issues.addComment(id, dto).then((c) => c.toDto()); } + @Post('/issues/:id/solution') + createSolution( + @Param('id', ParseUUIDPipe) id: string, + @Body() dto: CreateSolutionDto, + ) { + return this.solutions.create(id, dto).then((r) => r.toDto()); + } + + @Post('/issues/:id/solution/comments') + addSolutionComment( + @Body() dto: CreateSolutionCommentDto, + @Param('id', ParseUUIDPipe) id: string, + ) { + return this.solutions.addComment(id, dto).then((c) => c.toDto()); + } + @Post('/issues/:id/like') @HttpCode(HttpStatus.ACCEPTED) async addLike(@Param('id', ParseUUIDPipe) id: string) { diff --git a/apps/backend/src/solutions/dto/create-solution-comment.dto.ts b/apps/backend/src/solutions/dto/create-solution-comment.dto.ts new file mode 100644 index 0000000..a36e3c7 --- /dev/null +++ b/apps/backend/src/solutions/dto/create-solution-comment.dto.ts @@ -0,0 +1,13 @@ +import { IsString, MaxLength, MinLength } from 'class-validator'; + +export class CreateSolutionCommentDto { + @IsString() + @MinLength(3) + @MaxLength(16) + username: string; + + @IsString() + @MinLength(1) + @MaxLength(255) + content: string; +} diff --git a/apps/backend/src/solutions/entities/solution-comment.entity.ts b/apps/backend/src/solutions/entities/solution-comment.entity.ts new file mode 100644 index 0000000..ae0f1a1 --- /dev/null +++ b/apps/backend/src/solutions/entities/solution-comment.entity.ts @@ -0,0 +1,34 @@ +import { + Column, + CreateDateColumn, + Entity, + ManyToOne, + PrimaryGeneratedColumn, +} from 'typeorm'; +import { Solution } from './solution.entity'; + +@Entity('solution_comments') +export class SolutionComment { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + username: string; + + @Column() + content: string; + + @CreateDateColumn() + createdAt: Date; + + @ManyToOne(() => Solution, { lazy: true }) + solution: Promise; + + toDto() { + return { + username: this.username, + content: this.content, + createdAt: this.createdAt.toISOString(), + }; + } +} diff --git a/apps/backend/src/solutions/solutions.module.ts b/apps/backend/src/solutions/solutions.module.ts index b98060e..8a2f58d 100644 --- a/apps/backend/src/solutions/solutions.module.ts +++ b/apps/backend/src/solutions/solutions.module.ts @@ -3,9 +3,13 @@ import { SolutionsService } from './solutions.service'; import { IssuesModule } from 'src/issues/issues.module'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Solution } from './entities/solution.entity'; +import { SolutionComment } from './entities/solution-comment.entity'; @Module({ - imports: [IssuesModule, TypeOrmModule.forFeature([Solution])], + imports: [ + IssuesModule, + TypeOrmModule.forFeature([Solution, SolutionComment]), + ], providers: [SolutionsService], exports: [SolutionsService], }) diff --git a/apps/backend/src/solutions/solutions.service.ts b/apps/backend/src/solutions/solutions.service.ts index 44b9288..0369f11 100644 --- a/apps/backend/src/solutions/solutions.service.ts +++ b/apps/backend/src/solutions/solutions.service.ts @@ -1,15 +1,23 @@ -import { ConflictException, Injectable } from '@nestjs/common'; +import { + ConflictException, + Injectable, + NotFoundException, +} from '@nestjs/common'; import { CreateSolutionDto } from './dto/create-solution.dto'; import { IssuesService } from 'src/issues/issues.service'; import { InjectRepository } from '@nestjs/typeorm'; import { Solution } from './entities/solution.entity'; import { Repository } from 'typeorm'; +import { CreateSolutionCommentDto } from './dto/create-solution-comment.dto'; +import { SolutionComment } from './entities/solution-comment.entity'; @Injectable() export class SolutionsService { constructor( @InjectRepository(Solution) private readonly repository: Repository, + @InjectRepository(SolutionComment) + private readonly comments: Repository, private readonly issues: IssuesService, ) {} @@ -22,8 +30,15 @@ export class SolutionsService { return this.repository.save(solution); } - async getForIssue(issueId: string): Promise { - return this.repository.findOneBy({ issue: { id: issueId } }); + async getForIssue(issueId: string): Promise { + const ent = await this.repository.findOneBy({ issue: { id: issueId } }); + if (ent === null) { + throw new NotFoundException( + 'Solution for the given issue has not been found', + ); + } + + return ent; } async like(issueId: string, remove?: boolean) { @@ -37,4 +52,15 @@ export class SolutionsService { ent.dislikes = remove ? ent.dislikes - 1 : ent.dislikes + 1; return this.repository.save(ent); } + + async addComment(issueId: string, dto: CreateSolutionCommentDto) { + const ent = await this.getForIssue(issueId); + const comment = this.comments.create(dto); + comment.solution = Promise.resolve(ent); + return this.comments.save(comment); + } + + async getComments(id: string) { + return this.comments.findBy({ solution: { id } }); + } }