From eff879c1d81c1936140ce8bd508f1d5bdc47cf14 Mon Sep 17 00:00:00 2001 From: muntaxir4 Date: Tue, 10 Dec 2024 02:56:52 +0530 Subject: [PATCH 1/8] feat(api): Add endpoint to retrieve user workspace invitations with pagination --- .../controller/workspace.controller.ts | 22 +++++ .../workspace/service/workspace.service.ts | 97 +++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/apps/api/src/workspace/controller/workspace.controller.ts b/apps/api/src/workspace/controller/workspace.controller.ts index ac11b504..cd594429 100644 --- a/apps/api/src/workspace/controller/workspace.controller.ts +++ b/apps/api/src/workspace/controller/workspace.controller.ts @@ -44,6 +44,28 @@ export class WorkspaceController { return this.workspaceService.deleteWorkspace(user, workspaceSlug) } + @Get('invitations') + @RequiredApiKeyAuthorities(Authority.READ_WORKSPACE) + async getAllInvitationsOfUser( + @CurrentUser() user: User, + @Query('page') page: number = 0, + @Query('limit') limit: number = 10, + @Query('sort') sort: string = 'name', + @Query('order') order: string = 'asc', + @Query('search') search: string = '', + @Query('isAccepted') isAccepted: 'true' | 'false' | undefined = undefined + ) { + return this.workspaceService.getInvitationsOfUser( + user, + page, + limit, + sort, + order, + search, + isAccepted ? isAccepted === 'true' : undefined + ) + } + @Get(':workspaceSlug') @RequiredApiKeyAuthorities(Authority.READ_WORKSPACE) async getWorkspace( diff --git a/apps/api/src/workspace/service/workspace.service.ts b/apps/api/src/workspace/service/workspace.service.ts index 2a7ec59d..71df04c5 100644 --- a/apps/api/src/workspace/service/workspace.service.ts +++ b/apps/api/src/workspace/service/workspace.service.ts @@ -385,6 +385,103 @@ export class WorkspaceService { return { projects, environments, secrets, variables } } + /** + * Gets all workspaces of a user, paginated. + * @param user The user to get the workspaces for + * @param page The page number to get + * @param limit The number of items per page to get + * @param sort The field to sort by + * @param order The order to sort in + * @param search The search string to filter by + * @param isAccepted The invitation status to filter by + * @returns The workspace invitations of the user, paginated, with metadata + */ + async getInvitationsOfUser( + user: User, + page: number, + limit: number, + sort: string, + order: string, + search: string, + isAccepted: boolean | undefined + ) { + // fetch all workspaces of user where they are not admin + const items = await this.prisma.workspaceMember.findMany({ + skip: page * limit, + take: limitMaxItemsPerPage(Number(limit)), + orderBy: { + workspace: { + [sort]: order + } + }, + where: { + userId: user.id, + invitationAccepted: isAccepted, + roles: { + none: { + role: { + authorities: { + has: Authority.WORKSPACE_ADMIN + } + } + } + } + }, + select: { + workspace: { + select: { + id: true, + name: true, + slug: true, + icon: true + } + }, + roles: { + select: { + role: { + select: { + name: true + } + } + } + } + } + }) + + // get total count of workspaces of the user + const totalCount = await this.prisma.workspaceMember.count({ + where: { + userId: user.id, + invitationAccepted: isAccepted, + roles: { + none: { + role: { + authorities: { + has: Authority.WORKSPACE_ADMIN + } + } + } + } + } + }) + + //calculate metadata for pagination + const metadata = paginate( + totalCount, + `/workspace/invitations`, + { + page, + limit: limitMaxItemsPerPage(limit), + sort, + order, + search + }, + isAccepted !== undefined ? { isAccepted } : undefined + ) + + return { items, metadata } + } + /** * Gets a list of project IDs that the user has access to READ. * The user has access to a project if the project is global or if the user has the READ_PROJECT authority. From cc2a24278bee5735093237b13e2e0fd1efd8e2fd Mon Sep 17 00:00:00 2001 From: muntaxir4 Date: Tue, 10 Dec 2024 02:57:38 +0530 Subject: [PATCH 2/8] tests(api): Add tests for fetching workspace invitations with pagination --- apps/api/src/workspace/workspace.e2e.spec.ts | 75 +++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/apps/api/src/workspace/workspace.e2e.spec.ts b/apps/api/src/workspace/workspace.e2e.spec.ts index 5eba8c47..48ee3b31 100644 --- a/apps/api/src/workspace/workspace.e2e.spec.ts +++ b/apps/api/src/workspace/workspace.e2e.spec.ts @@ -35,6 +35,8 @@ import { SecretService } from '@/secret/service/secret.service' import { VariableService } from '@/variable/service/variable.service' import { WorkspaceRoleService } from '@/workspace-role/service/workspace-role.service' import { WorkspaceRoleModule } from '@/workspace-role/workspace-role.module' +import { WorkspaceMembershipService } from '@/workspace-membership/service/workspace-membership.service' +import { WorkspaceMembershipModule } from '@/workspace-membership/workspace-membership.module' import { fetchEvents } from '@/common/event' const createMembership = async ( @@ -71,6 +73,7 @@ describe('Workspace Controller Tests', () => { let secretService: SecretService let variableService: VariableService let workspaceRoleService: WorkspaceRoleService + let workspaceMembershipService: WorkspaceMembershipService let user1: User, user2: User let workspace1: Workspace, workspace2: Workspace @@ -87,7 +90,8 @@ describe('Workspace Controller Tests', () => { EnvironmentModule, SecretModule, VariableModule, - WorkspaceRoleModule + WorkspaceRoleModule, + WorkspaceMembershipModule ] }) .overrideProvider(MAIL_SERVICE) @@ -106,6 +110,7 @@ describe('Workspace Controller Tests', () => { secretService = moduleRef.get(SecretService) variableService = moduleRef.get(VariableService) workspaceRoleService = moduleRef.get(WorkspaceRoleService) + workspaceMembershipService = moduleRef.get(WorkspaceMembershipService) app.useGlobalPipes(new QueryTransformPipe()) @@ -179,6 +184,7 @@ describe('Workspace Controller Tests', () => { expect(secretService).toBeDefined() expect(variableService).toBeDefined() expect(workspaceRoleService).toBeDefined() + expect(workspaceMembershipService).toBeDefined() }) describe('Create Workspace Tests', () => { @@ -481,6 +487,73 @@ describe('Workspace Controller Tests', () => { }) }) + describe('Get All Workspace Invitations Tests', () => { + it('should be able to fetch all the workspace invitations of the user', async () => { + //invite user2 to workspace1 + await createMembership(memberRole.id, user2.id, workspace1.id, prisma) + + const response = await app.inject({ + method: 'GET', + headers: { + 'x-e2e-user-email': user2.email + }, + url: `/workspace/invitations` + }) + + const body = response.json() + + expect(body.items).toHaveLength(1) + expect(body.items[0].workspace.id).toBe(workspace1.id) + expect(body.items[0].workspace.slug).not.toBe(workspace2.slug) + expect(body.metadata.totalCount).toBe(1) + expect(body.metadata.links.self).toEqual( + `/workspace/invitations?page=0&limit=10&sort=name&order=asc&search=` + ) + expect(body.metadata.links.first).toEqual( + `/workspace/invitations?page=0&limit=10&sort=name&order=asc&search=` + ) + expect(body.metadata.links.previous).toBeNull() + expect(body.metadata.links.next).toBeNull() + expect(body.metadata.links.last).toEqual( + `/workspace/invitations?page=0&limit=10&sort=name&order=asc&search=` + ) + }) + + it('should be able to fetch all the workspace invitations of the user that are accepted', async () => { + //invite user2 to workspace1 + await createMembership(memberRole.id, user2.id, workspace1.id, prisma) + + // accept the invitation for user2 to workspace1 + await workspaceMembershipService.acceptInvitation(user2, workspace1.slug) + + const response = await app.inject({ + method: 'GET', + headers: { + 'x-e2e-user-email': user2.email + }, + url: `/workspace/invitations?isAccepted=true` + }) + + const body = response.json() + + expect(body.items).toHaveLength(1) + expect(body.items[0].workspace.id).toBe(workspace1.id) + expect(body.items[0].workspace.slug).not.toBe(workspace2.slug) + expect(body.metadata.totalCount).toBe(1) + expect(body.metadata.links.self).toEqual( + `/workspace/invitations?isAccepted=true&page=0&limit=10&sort=name&order=asc&search=` + ) + expect(body.metadata.links.first).toEqual( + `/workspace/invitations?isAccepted=true&page=0&limit=10&sort=name&order=asc&search=` + ) + expect(body.metadata.links.previous).toBeNull() + expect(body.metadata.links.next).toBeNull() + expect(body.metadata.links.last).toEqual( + `/workspace/invitations?isAccepted=true&page=0&limit=10&sort=name&order=asc&search=` + ) + }) + }) + describe('Export Data Tests', () => { it('should not be able to export data of a non-existing workspace', async () => { const response = await app.inject({ From 8cc2215dbf56195c5968f06332dc7b5a9af24245 Mon Sep 17 00:00:00 2001 From: muntaxir4 Date: Tue, 10 Dec 2024 03:06:15 +0530 Subject: [PATCH 3/8] refactor(api): Rename invitation method as per requirements --- apps/api/src/workspace/controller/workspace.controller.ts | 2 +- apps/api/src/workspace/service/workspace.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/src/workspace/controller/workspace.controller.ts b/apps/api/src/workspace/controller/workspace.controller.ts index cd594429..99ba5cb7 100644 --- a/apps/api/src/workspace/controller/workspace.controller.ts +++ b/apps/api/src/workspace/controller/workspace.controller.ts @@ -55,7 +55,7 @@ export class WorkspaceController { @Query('search') search: string = '', @Query('isAccepted') isAccepted: 'true' | 'false' | undefined = undefined ) { - return this.workspaceService.getInvitationsOfUser( + return this.workspaceService.getAllWorkspaceInvitations( user, page, limit, diff --git a/apps/api/src/workspace/service/workspace.service.ts b/apps/api/src/workspace/service/workspace.service.ts index 71df04c5..bf900eaa 100644 --- a/apps/api/src/workspace/service/workspace.service.ts +++ b/apps/api/src/workspace/service/workspace.service.ts @@ -396,7 +396,7 @@ export class WorkspaceService { * @param isAccepted The invitation status to filter by * @returns The workspace invitations of the user, paginated, with metadata */ - async getInvitationsOfUser( + async getAllWorkspaceInvitations( user: User, page: number, limit: number, From 6ce3489709c258134be448479ee97c0d6c16ec42 Mon Sep 17 00:00:00 2001 From: muntaxir4 Date: Wed, 11 Dec 2024 23:46:44 +0530 Subject: [PATCH 4/8] refactor(api): Add required changes to invitations; Add a test for invitation when workspace ownership is transferred --- .../controller/workspace.controller.ts | 6 +-- .../workspace/service/workspace.service.ts | 50 ++++++++--------- apps/api/src/workspace/workspace.e2e.spec.ts | 53 +++++++++++++------ 3 files changed, 63 insertions(+), 46 deletions(-) diff --git a/apps/api/src/workspace/controller/workspace.controller.ts b/apps/api/src/workspace/controller/workspace.controller.ts index 99ba5cb7..5356caa7 100644 --- a/apps/api/src/workspace/controller/workspace.controller.ts +++ b/apps/api/src/workspace/controller/workspace.controller.ts @@ -52,8 +52,7 @@ export class WorkspaceController { @Query('limit') limit: number = 10, @Query('sort') sort: string = 'name', @Query('order') order: string = 'asc', - @Query('search') search: string = '', - @Query('isAccepted') isAccepted: 'true' | 'false' | undefined = undefined + @Query('search') search: string = '' ) { return this.workspaceService.getAllWorkspaceInvitations( user, @@ -61,8 +60,7 @@ export class WorkspaceController { limit, sort, order, - search, - isAccepted ? isAccepted === 'true' : undefined + search ) } diff --git a/apps/api/src/workspace/service/workspace.service.ts b/apps/api/src/workspace/service/workspace.service.ts index bf900eaa..1c7754f0 100644 --- a/apps/api/src/workspace/service/workspace.service.ts +++ b/apps/api/src/workspace/service/workspace.service.ts @@ -386,14 +386,13 @@ export class WorkspaceService { } /** - * Gets all workspaces of a user, paginated. + * Gets all the invitations a user has to various workspaces, paginated. * @param user The user to get the workspaces for * @param page The page number to get * @param limit The number of items per page to get * @param sort The field to sort by * @param order The order to sort in * @param search The search string to filter by - * @param isAccepted The invitation status to filter by * @returns The workspace invitations of the user, paginated, with metadata */ async getAllWorkspaceInvitations( @@ -402,8 +401,7 @@ export class WorkspaceService { limit: number, sort: string, order: string, - search: string, - isAccepted: boolean | undefined + search: string ) { // fetch all workspaces of user where they are not admin const items = await this.prisma.workspaceMember.findMany({ @@ -416,13 +414,16 @@ export class WorkspaceService { }, where: { userId: user.id, - invitationAccepted: isAccepted, + invitationAccepted: false, + workspace: { + name: { + contains: search + } + }, roles: { none: { role: { - authorities: { - has: Authority.WORKSPACE_ADMIN - } + hasAdminAuthority: true } } } @@ -440,7 +441,8 @@ export class WorkspaceService { select: { role: { select: { - name: true + name: true, + colorCode: true } } } @@ -452,13 +454,16 @@ export class WorkspaceService { const totalCount = await this.prisma.workspaceMember.count({ where: { userId: user.id, - invitationAccepted: isAccepted, + invitationAccepted: false, + workspace: { + name: { + contains: search + } + }, roles: { none: { role: { - authorities: { - has: Authority.WORKSPACE_ADMIN - } + hasAdminAuthority: true } } } @@ -466,18 +471,13 @@ export class WorkspaceService { }) //calculate metadata for pagination - const metadata = paginate( - totalCount, - `/workspace/invitations`, - { - page, - limit: limitMaxItemsPerPage(limit), - sort, - order, - search - }, - isAccepted !== undefined ? { isAccepted } : undefined - ) + const metadata = paginate(totalCount, `/workspace/invitations`, { + page, + limit: limitMaxItemsPerPage(limit), + sort, + order, + search + }) return { items, metadata } } diff --git a/apps/api/src/workspace/workspace.e2e.spec.ts b/apps/api/src/workspace/workspace.e2e.spec.ts index 48ee3b31..eb15bd00 100644 --- a/apps/api/src/workspace/workspace.e2e.spec.ts +++ b/apps/api/src/workspace/workspace.e2e.spec.ts @@ -488,7 +488,7 @@ describe('Workspace Controller Tests', () => { }) describe('Get All Workspace Invitations Tests', () => { - it('should be able to fetch all the workspace invitations of the user', async () => { + it('should be able to fetch all the non accepted invitations of the user', async () => { //invite user2 to workspace1 await createMembership(memberRole.id, user2.id, workspace1.id, prisma) @@ -519,7 +519,7 @@ describe('Workspace Controller Tests', () => { ) }) - it('should be able to fetch all the workspace invitations of the user that are accepted', async () => { + it('should be able to fetch empty list of workspace invitations for the user once all invitations are accepted', async () => { //invite user2 to workspace1 await createMembership(memberRole.id, user2.id, workspace1.id, prisma) @@ -531,26 +531,45 @@ describe('Workspace Controller Tests', () => { headers: { 'x-e2e-user-email': user2.email }, - url: `/workspace/invitations?isAccepted=true` + url: `/workspace/invitations` }) const body = response.json() + expect(body.items).toHaveLength(0) + expect(body.metadata).toEqual({}) + }) - expect(body.items).toHaveLength(1) - expect(body.items[0].workspace.id).toBe(workspace1.id) - expect(body.items[0].workspace.slug).not.toBe(workspace2.slug) - expect(body.metadata.totalCount).toBe(1) - expect(body.metadata.links.self).toEqual( - `/workspace/invitations?isAccepted=true&page=0&limit=10&sort=name&order=asc&search=` - ) - expect(body.metadata.links.first).toEqual( - `/workspace/invitations?isAccepted=true&page=0&limit=10&sort=name&order=asc&search=` - ) - expect(body.metadata.links.previous).toBeNull() - expect(body.metadata.links.next).toBeNull() - expect(body.metadata.links.last).toEqual( - `/workspace/invitations?isAccepted=true&page=0&limit=10&sort=name&order=asc&search=` + it('should be able to fetch empty list of workspace invitations for the user if ownership is transferred', async () => { + //create a new workspace for user 1 + const workspace3 = await workspaceService.createWorkspace(user1, { + name: 'Workspace 3' + }) + + //invite user2 to workspace3 + await createMembership(memberRole.id, user2.id, workspace3.id, prisma) + + //accept the invitation for user2 to workspace3 + await workspaceMembershipService.acceptInvitation(user2, workspace3.slug) + + //transfer ownership of workspace1 to user2 + await workspaceMembershipService.transferOwnership( + user1, + workspace3.slug, + user2.email ) + + const response = await app.inject({ + method: 'GET', + headers: { + 'x-e2e-user-email': user1.email + }, + url: `/workspace/invitations` + }) + + const body = response.json() + console.log(body) + expect(body.items).toHaveLength(0) + expect(body.metadata).toEqual({}) }) }) From c233a9cbed6acb6051305b6effcbbc0cdac3a5dc Mon Sep 17 00:00:00 2001 From: muntaxir4 Date: Wed, 11 Dec 2024 05:28:20 +0530 Subject: [PATCH 5/8] tests(api): fix environment test unsucessfull when ran individually Related to #583 --- .../environment/dto/create.environment/create.environment.ts | 1 - apps/api/src/environment/environment.e2e.spec.ts | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/api/src/environment/dto/create.environment/create.environment.ts b/apps/api/src/environment/dto/create.environment/create.environment.ts index ffa62e3f..ae2b7db8 100644 --- a/apps/api/src/environment/dto/create.environment/create.environment.ts +++ b/apps/api/src/environment/dto/create.environment/create.environment.ts @@ -3,7 +3,6 @@ import { IsNotEmpty, IsOptional, IsString, Matches } from 'class-validator' export class CreateEnvironment { @IsString() @IsNotEmpty() - @Matches(/^[a-zA-Z0-9-_]{1,64}$/) name: string @IsString() diff --git a/apps/api/src/environment/environment.e2e.spec.ts b/apps/api/src/environment/environment.e2e.spec.ts index ad949f1c..607705ef 100644 --- a/apps/api/src/environment/environment.e2e.spec.ts +++ b/apps/api/src/environment/environment.e2e.spec.ts @@ -28,6 +28,7 @@ import { UserModule } from '@/user/user.module' import { UserService } from '@/user/service/user.service' import { QueryTransformPipe } from '@/common/pipes/query.transform.pipe' import { fetchEvents } from '@/common/event' +import { ValidationPipe } from '@nestjs/common' describe('Environment Controller Tests', () => { let app: NestFastifyApplication @@ -65,7 +66,7 @@ describe('Environment Controller Tests', () => { environmentService = moduleRef.get(EnvironmentService) userService = moduleRef.get(UserService) - app.useGlobalPipes(new QueryTransformPipe()) + app.useGlobalPipes(new ValidationPipe(), new QueryTransformPipe()) await app.init() await app.getHttpAdapter().getInstance().ready() @@ -184,7 +185,7 @@ describe('Environment Controller Tests', () => { 'x-e2e-user-email': user1.email } }) - + expect(response.statusCode).toBe(400) expect(response.json().message).toContain('name should not be empty') }) From b02fbf7c595ef06779463a0b3074c5256c395503 Mon Sep 17 00:00:00 2001 From: muntaxir4 Date: Thu, 12 Dec 2024 02:53:24 +0530 Subject: [PATCH 6/8] feat(api): add createdOn field to WorkspaceMember model and update related services --- .../migration.sql | 2 ++ apps/api/src/prisma/schema.prisma | 1 + .../service/workspace-membership.service.ts | 5 ++++- .../workspace-membership.e2e.spec.ts | 6 +++-- .../workspace/service/workspace.service.ts | 12 ++++++++-- apps/api/src/workspace/workspace.e2e.spec.ts | 22 ++++++++++++++++--- 6 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 apps/api/src/prisma/migrations/20241211205448_add_created_on_in_workspace_member/migration.sql diff --git a/apps/api/src/prisma/migrations/20241211205448_add_created_on_in_workspace_member/migration.sql b/apps/api/src/prisma/migrations/20241211205448_add_created_on_in_workspace_member/migration.sql new file mode 100644 index 00000000..ded8dff4 --- /dev/null +++ b/apps/api/src/prisma/migrations/20241211205448_add_created_on_in_workspace_member/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "WorkspaceMember" ADD COLUMN "createdOn" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/apps/api/src/prisma/schema.prisma b/apps/api/src/prisma/schema.prisma index 5aaa434c..d64780ef 100644 --- a/apps/api/src/prisma/schema.prisma +++ b/apps/api/src/prisma/schema.prisma @@ -339,6 +339,7 @@ model WorkspaceMember { workspaceId String invitationAccepted Boolean @default(false) roles WorkspaceMemberRoleAssociation[] + createdOn DateTime @default(now()) @@unique([workspaceId, userId]) } diff --git a/apps/api/src/workspace-membership/service/workspace-membership.service.ts b/apps/api/src/workspace-membership/service/workspace-membership.service.ts index d10032c5..6bcf5d89 100644 --- a/apps/api/src/workspace-membership/service/workspace-membership.service.ts +++ b/apps/api/src/workspace-membership/service/workspace-membership.service.ts @@ -879,11 +879,14 @@ export class WorkspaceMembershipService { roleSet.add(role) } + const invitedOn = new Date() + // Create the workspace membership const createMembership = this.prisma.workspaceMember.create({ data: { workspaceId: workspace.id, userId, + createdOn: invitedOn, roles: { create: Array.from(roleSet).map((role) => ({ role: { @@ -904,7 +907,7 @@ export class WorkspaceMembershipService { workspace.name, `${process.env.WORKSPACE_FRONTEND_URL}/workspace/${workspace.slug}/join`, currentUser.name, - new Date().toISOString(), + invitedOn.toISOString(), true ) diff --git a/apps/api/src/workspace-membership/workspace-membership.e2e.spec.ts b/apps/api/src/workspace-membership/workspace-membership.e2e.spec.ts index a606a17d..9c1222c6 100644 --- a/apps/api/src/workspace-membership/workspace-membership.e2e.spec.ts +++ b/apps/api/src/workspace-membership/workspace-membership.e2e.spec.ts @@ -425,7 +425,8 @@ describe('Workspace Membership Controller Tests', () => { id: expect.any(String), userId: user2.id, workspaceId: workspace1.id, - invitationAccepted: false + invitationAccepted: false, + createdOn: expect.any(Date) }) }) @@ -909,7 +910,8 @@ describe('Workspace Membership Controller Tests', () => { id: expect.any(String), userId: user2.id, workspaceId: workspace1.id, - invitationAccepted: true + invitationAccepted: true, + createdOn: expect.any(Date) }) }) diff --git a/apps/api/src/workspace/service/workspace.service.ts b/apps/api/src/workspace/service/workspace.service.ts index 1c7754f0..b965a7d3 100644 --- a/apps/api/src/workspace/service/workspace.service.ts +++ b/apps/api/src/workspace/service/workspace.service.ts @@ -446,7 +446,8 @@ export class WorkspaceService { } } } - } + }, + createdOn: true } }) @@ -479,7 +480,14 @@ export class WorkspaceService { search }) - return { items, metadata } + return { + items: items.map((item) => ({ + ...item, + invitedOn: item.createdOn, + createdOn: undefined + })), + metadata + } } /** diff --git a/apps/api/src/workspace/workspace.e2e.spec.ts b/apps/api/src/workspace/workspace.e2e.spec.ts index eb15bd00..c452124b 100644 --- a/apps/api/src/workspace/workspace.e2e.spec.ts +++ b/apps/api/src/workspace/workspace.e2e.spec.ts @@ -311,7 +311,8 @@ describe('Workspace Controller Tests', () => { id: expect.any(String), userId: user1.id, workspaceId: workspace1.id, - invitationAccepted: true + invitationAccepted: true, + createdOn: expect.any(Date) }) }) }) @@ -503,8 +504,24 @@ describe('Workspace Controller Tests', () => { const body = response.json() expect(body.items).toHaveLength(1) - expect(body.items[0].workspace.id).toBe(workspace1.id) expect(body.items[0].workspace.slug).not.toBe(workspace2.slug) + expect(body.items[0]).toEqual({ + invitedOn: expect.any(Date.toString()), + workspace: { + icon: workspace1.icon, + id: workspace1.id, + name: workspace1.name, + slug: workspace1.slug + }, + roles: [ + { + role: { + name: memberRole.name, + colorCode: memberRole.colorCode + } + } + ] + }) expect(body.metadata.totalCount).toBe(1) expect(body.metadata.links.self).toEqual( `/workspace/invitations?page=0&limit=10&sort=name&order=asc&search=` @@ -567,7 +584,6 @@ describe('Workspace Controller Tests', () => { }) const body = response.json() - console.log(body) expect(body.items).toHaveLength(0) expect(body.metadata).toEqual({}) }) From 53aa3c36da7b126e8874f64a07544949532c94c9 Mon Sep 17 00:00:00 2001 From: muntaxir4 Date: Thu, 12 Dec 2024 03:06:49 +0530 Subject: [PATCH 7/8] fix(api): update invitedOn type expectation in tests --- apps/api/src/workspace/workspace.e2e.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/workspace/workspace.e2e.spec.ts b/apps/api/src/workspace/workspace.e2e.spec.ts index c452124b..30960866 100644 --- a/apps/api/src/workspace/workspace.e2e.spec.ts +++ b/apps/api/src/workspace/workspace.e2e.spec.ts @@ -506,7 +506,7 @@ describe('Workspace Controller Tests', () => { expect(body.items).toHaveLength(1) expect(body.items[0].workspace.slug).not.toBe(workspace2.slug) expect(body.items[0]).toEqual({ - invitedOn: expect.any(Date.toString()), + invitedOn: expect.any(String), workspace: { icon: workspace1.icon, id: workspace1.id, From 4d3b5287e3db46f27b23669cf146e23bad243ba2 Mon Sep 17 00:00:00 2001 From: muntaxir4 Date: Fri, 13 Dec 2024 01:11:51 +0530 Subject: [PATCH 8/8] feat(docs): Add bru doc for workspace invitations --- .../Get all invitations of user.bru | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 api-collection/Workspace Controller/Get all invitations of user.bru diff --git a/api-collection/Workspace Controller/Get all invitations of user.bru b/api-collection/Workspace Controller/Get all invitations of user.bru new file mode 100644 index 00000000..7912da15 --- /dev/null +++ b/api-collection/Workspace Controller/Get all invitations of user.bru @@ -0,0 +1,21 @@ +meta { + name: Get all invitations of user to workspaces + type: http + seq: 3 +} + +get { + url: {{BASE_URL}}/api/workspace/invitations?page=0&limit=10 + body: none + auth: bearer +} + +auth:bearer { + token: {{JWT}} +} + +docs { + ## Description + + Fetches all the workspaces where the user is invited to. +}