From 18f61608524760607bbfdfa61d4a28c66cf68773 Mon Sep 17 00:00:00 2001 From: LeeJongBeom <52884648+devleejb@users.noreply.github.com> Date: Thu, 18 Jan 2024 14:30:51 +0900 Subject: [PATCH] (BE) Add API for Creating a Document in Workspace (#61) * Add base code for workspace documents * Fix lint & Add service for workspace document * Add controller for creating a workspace document --- backend/src/app.module.ts | 2 + .../dto/create-workspace-document.dto.ts | 6 +++ ...create-workspace-document-response.type.ts | 3 ++ .../types/workspace-document-domain.type.ts | 18 ++++++++ .../workspace-documents.controller.spec.ts | 18 ++++++++ .../workspace-documents.controller.ts | 44 +++++++++++++++++++ .../workspace-documents.module.ts | 10 +++++ .../workspace-documents.service.spec.ts | 18 ++++++++ .../workspace-documents.service.ts | 28 ++++++++++++ .../workspace-users.controller.ts | 14 +++++- .../src/workspaces/workspaces.controller.ts | 2 +- 11 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 backend/src/workspace-documents/dto/create-workspace-document.dto.ts create mode 100644 backend/src/workspace-documents/types/create-workspace-document-response.type.ts create mode 100644 backend/src/workspace-documents/types/workspace-document-domain.type.ts create mode 100644 backend/src/workspace-documents/workspace-documents.controller.spec.ts create mode 100644 backend/src/workspace-documents/workspace-documents.controller.ts create mode 100644 backend/src/workspace-documents/workspace-documents.module.ts create mode 100644 backend/src/workspace-documents/workspace-documents.service.spec.ts create mode 100644 backend/src/workspace-documents/workspace-documents.service.ts diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 73567ed1..03d1b6b3 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -7,6 +7,7 @@ import { APP_GUARD } from "@nestjs/core/constants"; import { JwtAuthGuard } from "./auth/jwt.guard"; import { WorkspacesModule } from "./workspaces/workspaces.module"; import { WorkspaceUsersModule } from "./workspace-users/workspace-users.module"; +import { WorkspaceDocumentsModule } from "./workspace-documents/workspace-documents.module"; @Module({ imports: [ @@ -15,6 +16,7 @@ import { WorkspaceUsersModule } from "./workspace-users/workspace-users.module"; AuthModule, WorkspacesModule, WorkspaceUsersModule, + WorkspaceDocumentsModule, ], controllers: [], providers: [ diff --git a/backend/src/workspace-documents/dto/create-workspace-document.dto.ts b/backend/src/workspace-documents/dto/create-workspace-document.dto.ts new file mode 100644 index 00000000..d6b88b32 --- /dev/null +++ b/backend/src/workspace-documents/dto/create-workspace-document.dto.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class CreateWorkspaceDocumentDto { + @ApiProperty({ description: "Title of document to create", type: String }) + title: string; +} diff --git a/backend/src/workspace-documents/types/create-workspace-document-response.type.ts b/backend/src/workspace-documents/types/create-workspace-document-response.type.ts new file mode 100644 index 00000000..9e4aac1b --- /dev/null +++ b/backend/src/workspace-documents/types/create-workspace-document-response.type.ts @@ -0,0 +1,3 @@ +import { WorkspaceDocumentDomain } from "./workspace-document-domain.type"; + +export class CreateWorkspaceDocumentResponse extends WorkspaceDocumentDomain {} diff --git a/backend/src/workspace-documents/types/workspace-document-domain.type.ts b/backend/src/workspace-documents/types/workspace-document-domain.type.ts new file mode 100644 index 00000000..c6c7b338 --- /dev/null +++ b/backend/src/workspace-documents/types/workspace-document-domain.type.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class WorkspaceDocumentDomain { + @ApiProperty({ type: String, description: "ID of the document" }) + id: string; + @ApiProperty({ type: String, description: "Yorkie Document ID of the document" }) + yorkieDocumentId: string; + @ApiProperty({ type: String, description: "Title of the document" }) + title: string; + @ApiProperty({ type: String, description: "Content of the document", required: false }) + content?: string; + @ApiProperty({ type: Date, description: "Created date of the document" }) + createdAt: Date; + @ApiProperty({ type: Date, description: "Updated date of the document" }) + updatedAt: Date; + @ApiProperty({ type: String, description: "ID of the workspace that includes the document" }) + workspaceId: string; +} diff --git a/backend/src/workspace-documents/workspace-documents.controller.spec.ts b/backend/src/workspace-documents/workspace-documents.controller.spec.ts new file mode 100644 index 00000000..eaedd6bc --- /dev/null +++ b/backend/src/workspace-documents/workspace-documents.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { WorkspaceDocumentsController } from "./workspace-documents.controller"; + +describe("WorkspaceDocumentsController", () => { + let controller: WorkspaceDocumentsController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [WorkspaceDocumentsController], + }).compile(); + + controller = module.get(WorkspaceDocumentsController); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/backend/src/workspace-documents/workspace-documents.controller.ts b/backend/src/workspace-documents/workspace-documents.controller.ts new file mode 100644 index 00000000..c78f7df5 --- /dev/null +++ b/backend/src/workspace-documents/workspace-documents.controller.ts @@ -0,0 +1,44 @@ +import { Body, Controller, Param, Post, Req } from "@nestjs/common"; +import { WorkspaceDocumentsService } from "./workspace-documents.service"; +import { + ApiBearerAuth, + ApiBody, + ApiCreatedResponse, + ApiNotFoundResponse, + ApiOperation, + ApiTags, +} from "@nestjs/swagger"; +import { AuthroizedRequest } from "src/utils/types/req.type"; +import { CreateWorkspaceDocumentDto } from "./dto/create-workspace-document.dto"; +import { CreateWorkspaceDocumentResponse } from "./types/create-workspace-document-response.type"; +import { HttpExceptionResponse } from "src/utils/types/http-exception-response.type"; + +@ApiTags("Workspace.Documents") +@ApiBearerAuth() +@Controller("workspaces/:workspace_id/documents") +export class WorkspaceDocumentsController { + constructor(private workspaceDocumentsService: WorkspaceDocumentsService) {} + + @Post() + @ApiOperation({ + summary: "Create a Document in a Workspace", + description: "Create a document with the title in a workspace", + }) + @ApiBody({ type: CreateWorkspaceDocumentDto }) + @ApiCreatedResponse({ type: CreateWorkspaceDocumentResponse }) + @ApiNotFoundResponse({ + type: HttpExceptionResponse, + description: "The workspace does not exist, or the user lacks the appropriate permissions.", + }) + async create( + @Req() req: AuthroizedRequest, + @Param("workspace_id") workspaceId: string, + @Body() createWorkspaceDocumentDto: CreateWorkspaceDocumentDto + ): Promise { + return this.workspaceDocumentsService.create( + req.user.id, + workspaceId, + createWorkspaceDocumentDto.title + ); + } +} diff --git a/backend/src/workspace-documents/workspace-documents.module.ts b/backend/src/workspace-documents/workspace-documents.module.ts new file mode 100644 index 00000000..1d50449a --- /dev/null +++ b/backend/src/workspace-documents/workspace-documents.module.ts @@ -0,0 +1,10 @@ +import { Module } from "@nestjs/common"; +import { WorkspaceDocumentsService } from "./workspace-documents.service"; +import { WorkspaceDocumentsController } from "./workspace-documents.controller"; +import { PrismaService } from "src/db/prisma.service"; + +@Module({ + providers: [WorkspaceDocumentsService, PrismaService], + controllers: [WorkspaceDocumentsController], +}) +export class WorkspaceDocumentsModule {} diff --git a/backend/src/workspace-documents/workspace-documents.service.spec.ts b/backend/src/workspace-documents/workspace-documents.service.spec.ts new file mode 100644 index 00000000..c6c3125e --- /dev/null +++ b/backend/src/workspace-documents/workspace-documents.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { WorkspaceDocumentsService } from "./workspace-documents.service"; + +describe("WorkspaceDocumentsService", () => { + let service: WorkspaceDocumentsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [WorkspaceDocumentsService], + }).compile(); + + service = module.get(WorkspaceDocumentsService); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); +}); diff --git a/backend/src/workspace-documents/workspace-documents.service.ts b/backend/src/workspace-documents/workspace-documents.service.ts new file mode 100644 index 00000000..8479bd00 --- /dev/null +++ b/backend/src/workspace-documents/workspace-documents.service.ts @@ -0,0 +1,28 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { PrismaService } from "src/db/prisma.service"; + +@Injectable() +export class WorkspaceDocumentsService { + constructor(private prismaService: PrismaService) {} + + async create(userId: string, workspaceId: string, title: string) { + try { + await this.prismaService.userWorkspace.findFirstOrThrow({ + where: { + userId, + workspaceId, + }, + }); + } catch (e) { + throw new NotFoundException(); + } + + return this.prismaService.document.create({ + data: { + title, + workspaceId, + yorkieDocumentId: Math.random().toString(36).substring(7), + }, + }); + } +} diff --git a/backend/src/workspace-users/workspace-users.controller.ts b/backend/src/workspace-users/workspace-users.controller.ts index a73ab22f..1788aa5c 100644 --- a/backend/src/workspace-users/workspace-users.controller.ts +++ b/backend/src/workspace-users/workspace-users.controller.ts @@ -1,8 +1,16 @@ import { Controller, DefaultValuePipe, Get, Param, ParseIntPipe, Query, Req } from "@nestjs/common"; -import { ApiBearerAuth, ApiFoundResponse, ApiOperation, ApiQuery, ApiTags } from "@nestjs/swagger"; +import { + ApiBearerAuth, + ApiFoundResponse, + ApiNotFoundResponse, + ApiOperation, + ApiQuery, + ApiTags, +} from "@nestjs/swagger"; import { FindWorkspaceUsersResponse } from "./types/find-workspace-users-response.type"; import { AuthroizedRequest } from "src/utils/types/req.type"; import { WorkspaceUsersService } from "./workspace-users.service"; +import { HttpExceptionResponse } from "src/utils/types/http-exception-response.type"; @ApiTags("Workspace.Users") @ApiBearerAuth() @@ -16,6 +24,10 @@ export class WorkspaceUsersController { description: "Return the users in the workspace. This API supports KeySet pagination.", }) @ApiFoundResponse({ type: FindWorkspaceUsersResponse }) + @ApiNotFoundResponse({ + type: HttpExceptionResponse, + description: "The workspace does not exist, or the user lacks the appropriate permissions.", + }) @ApiQuery({ name: "page_size", type: Number, diff --git a/backend/src/workspaces/workspaces.controller.ts b/backend/src/workspaces/workspaces.controller.ts index 3256cc41..ee1f94ba 100644 --- a/backend/src/workspaces/workspaces.controller.ts +++ b/backend/src/workspaces/workspaces.controller.ts @@ -55,7 +55,7 @@ export class WorkspacesController { @ApiFoundResponse({ type: FindWorkspaceResponse }) @ApiNotFoundResponse({ type: HttpExceptionResponse, - description: "The Workspace does not exist, or the user lacks the appropriate permissions.", + description: "The workspace does not exist, or the user lacks the appropriate permissions.", }) async findOne( @Req() req: AuthroizedRequest,