diff --git a/backend/src/workspaces/types/create-workspace-response.type.ts b/backend/src/workspaces/types/create-workspace-response.type.ts index f462cc9e..c5e79ab2 100644 --- a/backend/src/workspaces/types/create-workspace-response.type.ts +++ b/backend/src/workspaces/types/create-workspace-response.type.ts @@ -1,12 +1,3 @@ -import { ApiProperty } from "@nestjs/swagger"; +import { WorkspaceDomain } from "./workspace-domain.type"; -export class CreateWorkspaceResponse { - @ApiProperty({ type: String, description: "ID of new workspace" }) - id: string; - @ApiProperty({ type: String, description: "Title of new workspace" }) - title: string; - @ApiProperty({ type: Date, description: "Created date of new workspace" }) - createdAt: Date; - @ApiProperty({ type: Date, description: "Updated date of new workspace" }) - updatedAt: Date; -} +export class CreateWorkspaceResponse extends WorkspaceDomain {} diff --git a/backend/src/workspaces/types/find-workspace-response.type.ts b/backend/src/workspaces/types/find-workspace-response.type.ts index 8a073d24..975538b6 100644 --- a/backend/src/workspaces/types/find-workspace-response.type.ts +++ b/backend/src/workspaces/types/find-workspace-response.type.ts @@ -1,12 +1,3 @@ -import { ApiProperty } from "@nestjs/swagger"; +import { WorkspaceDomain } from "./workspace-domain.type"; -export class FindWorkspaceResponse { - @ApiProperty({ type: String, description: "ID of found workspace" }) - id: string; - @ApiProperty({ type: String, description: "Title of found workspace" }) - title: string; - @ApiProperty({ type: Date, description: "Created date of found workspace" }) - createdAt: Date; - @ApiProperty({ type: Date, description: "Updated date of found workspace" }) - updatedAt: Date; -} +export class FindWorkspaceResponse extends WorkspaceDomain {} diff --git a/backend/src/workspaces/types/find-workspaces-response.type.ts b/backend/src/workspaces/types/find-workspaces-response.type.ts new file mode 100644 index 00000000..5adcecf0 --- /dev/null +++ b/backend/src/workspaces/types/find-workspaces-response.type.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { WorkspaceDomain } from "./workspace-domain.type"; + +export class FindWorkspacesResponse { + @ApiProperty({ type: [WorkspaceDomain], description: "List of found workspaces" }) + workspaces: Array; + + @ApiProperty({ type: String, description: "The ID of last workspace" }) + cursor: string | null; +} diff --git a/backend/src/workspaces/types/workspace-domain.type.ts b/backend/src/workspaces/types/workspace-domain.type.ts new file mode 100644 index 00000000..953d310b --- /dev/null +++ b/backend/src/workspaces/types/workspace-domain.type.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class WorkspaceDomain { + @ApiProperty({ type: String, description: "ID of the workspace" }) + id: string; + @ApiProperty({ type: String, description: "Title of the workspace" }) + title: string; + @ApiProperty({ type: Date, description: "Created date of the workspace" }) + createdAt: Date; + @ApiProperty({ type: Date, description: "Updated date of the workspace" }) + updatedAt: Date; +} diff --git a/backend/src/workspaces/workspaces.controller.ts b/backend/src/workspaces/workspaces.controller.ts index 1c27aa61..3256cc41 100644 --- a/backend/src/workspaces/workspaces.controller.ts +++ b/backend/src/workspaces/workspaces.controller.ts @@ -1,4 +1,14 @@ -import { Body, Controller, Get, Param, Post, Req } from "@nestjs/common"; +import { + Body, + Controller, + DefaultValuePipe, + Get, + Param, + ParseIntPipe, + Post, + Query, + Req, +} from "@nestjs/common"; import { WorkspacesService } from "./workspaces.service"; import { CreateWorkspaceDto } from "./dto/CreateWorkspace.dto"; import { @@ -8,12 +18,14 @@ import { ApiFoundResponse, ApiNotFoundResponse, ApiOperation, + ApiQuery, ApiTags, } from "@nestjs/swagger"; import { AuthroizedRequest } from "src/utils/types/req.type"; import { CreateWorkspaceResponse } from "./types/create-workspace-response.type"; import { FindWorkspaceResponse } from "./types/find-workspace-response.type"; import { HttpExceptionResponse } from "src/utils/types/http-exception-response.type"; +import { FindWorkspacesResponse } from "./types/find-workspaces-response.type"; @ApiTags("Workspaces") @ApiBearerAuth() @@ -45,7 +57,37 @@ export class WorkspacesController { type: HttpExceptionResponse, description: "The Workspace does not exist, or the user lacks the appropriate permissions.", }) - async findOne(@Req() req: AuthroizedRequest, @Param("id") workspaceId: string) { + async findOne( + @Req() req: AuthroizedRequest, + @Param("id") workspaceId: string + ): Promise { return this.workspacesService.findOne(req.user.id, workspaceId); } + + @Get("") + @ApiOperation({ + summary: "Retrieve the Workspaces", + description: "Return the user's workspaces. This API supports KeySet pagination.", + }) + @ApiFoundResponse({ type: FindWorkspacesResponse }) + @ApiQuery({ + name: "page_size", + type: Number, + description: "Page size to fetch (Default to 10)", + required: false, + }) + @ApiQuery({ + name: "cursor", + type: String, + description: + "API returns a limited set of results after a given cursor. If no value is provided, it returns the first page.", + required: false, + }) + async findMany( + @Req() req: AuthroizedRequest, + @Query("page_size", new DefaultValuePipe(10), ParseIntPipe) pageSize: number, + @Query("cursor", new DefaultValuePipe(undefined)) cursor?: string + ): Promise { + return this.workspacesService.findMany(req.user.id, pageSize, cursor); + } } diff --git a/backend/src/workspaces/workspaces.service.ts b/backend/src/workspaces/workspaces.service.ts index ed5a285a..2fe27cde 100644 --- a/backend/src/workspaces/workspaces.service.ts +++ b/backend/src/workspaces/workspaces.service.ts @@ -1,6 +1,7 @@ import { Injectable, NotFoundException } from "@nestjs/common"; -import { Workspace } from "@prisma/client"; +import { Prisma, Workspace } from "@prisma/client"; import { PrismaService } from "src/db/prisma.service"; +import { FindWorkspacesResponse } from "./types/find-workspaces-response.type"; @Injectable() export class WorkspacesService { @@ -42,4 +43,38 @@ export class WorkspacesService { }, }); } + + async findMany( + userId: string, + pageSize: number, + cursor?: string + ): Promise { + const additionalOptions: Prisma.WorkspaceFindManyArgs = {}; + + if (cursor) { + additionalOptions.cursor = { id: cursor }; + } + + const workspaceList = await this.prismaService.workspace.findMany({ + take: pageSize + 1, + where: { + userWorkspaceList: { + some: { + userId: { + equals: userId, + }, + }, + }, + }, + orderBy: { + id: "desc", + }, + ...additionalOptions, + }); + + return { + workspaces: workspaceList.slice(0, pageSize), + cursor: workspaceList.length > pageSize ? workspaceList[pageSize].id : null, + }; + } }