From 6a1c091ceabe012eabd247eee04fd9a67717ffec Mon Sep 17 00:00:00 2001 From: Anonymus2000 <54113952+Nil2000@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:59:29 +0530 Subject: [PATCH] feat(api-client): Added workspace-membership and related tests (#452) --- .../src/controllers/workspace-membership.ts | 149 +++++++++++++++++ packages/api-client/src/index.ts | 4 +- .../src/types/workspace-membership.types.d.ts | 145 ++++++++++++++++ .../tests/workspace-membership.spec.ts | 158 ++++++++++++++++++ 4 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 packages/api-client/src/controllers/workspace-membership.ts create mode 100644 packages/api-client/src/types/workspace-membership.types.d.ts create mode 100644 packages/api-client/tests/workspace-membership.spec.ts diff --git a/packages/api-client/src/controllers/workspace-membership.ts b/packages/api-client/src/controllers/workspace-membership.ts new file mode 100644 index 00000000..f6ec72fe --- /dev/null +++ b/packages/api-client/src/controllers/workspace-membership.ts @@ -0,0 +1,149 @@ +import { APIClient } from '@api-client/core/client' +import { parseResponse } from '@api-client/core/response-parser' +import { parsePaginationUrl } from '@api-client/core/pagination-parser' +import { + TransferOwnershipRequest, + TransferOwnershipResponse, + InviteUsersRequest, + InviteUsersResponse, + RemoveUsersRequest, + RemoveUsersResponse, + UpdateMemberRoleRequest, + UpdateMemberRoleResponse, + AcceptInvitationRequest, + AcceptInvitationResponse, + DeclineInvitationRequest, + DeclineInvitationResponse, + CancelInvitationRequest, + CancelInvitationResponse, + LeaveWorkspaceRequest, + LeaveWorkspaceResponse, + IsMemberRequest, + IsMemberResponse, + GetMembersRequest, + GetMembersResponse +} from '@api-client/types/workspace-membership.types' +import { ClientResponse } from '@api-client/types/index.types' + +export default class WorkspaceMembershipController { + private apiClient: APIClient + + constructor(private readonly backendUrl: string) { + this.apiClient = new APIClient(this.backendUrl) + } + + async transferOwnership( + request: TransferOwnershipRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.put( + `/api/workspace-membership/${request.workspaceSlug}/transfer-ownership/${request.userEmail}`, + request, + headers + ) + return await parseResponse(response) + } + + async inviteUsers( + request: InviteUsersRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.post( + `/api/workspace-membership/${request.workspaceSlug}/invite-users`, + request, + headers + ) + return await parseResponse(response) + } + + async removeUsers( + request: RemoveUsersRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.delete( + `/api/workspace-membership/${request.workspaceSlug}/remove-users`, + headers + ) + return await parseResponse(response) + } + + async updateMemberRoles( + request: UpdateMemberRoleRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.put( + `/api/workspace-membership/${request.workspaceSlug}/update-member-role/${request.userEmail}`, + request, + headers + ) + return await parseResponse(response) + } + + async acceptInvitation( + request: AcceptInvitationRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.post( + `/api/workspace-membership/${request.workspaceSlug}/accept-invitation`, + request, + headers + ) + return await parseResponse(response) + } + + async declineInvitation( + request: DeclineInvitationRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.delete( + `/api/workspace-membership/${request.workspaceSlug}/decline-invitation`, + headers + ) + return await parseResponse(response) + } + + async cancelInvitation( + request: CancelInvitationRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.delete( + `/api/workspace-membership/${request.workspaceSlug}/cancel-invitation/${request.userEmail}`, + headers + ) + return await parseResponse(response) + } + + async leaveWorkspace( + request: LeaveWorkspaceRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.delete( + `/api/workspace-membership/${request.workspaceSlug}/leave`, + headers + ) + return await parseResponse(response) + } + + async isMember( + request: IsMemberRequest, + headers?: Record + ): Promise> { + const response = await this.apiClient.get( + `/api/workspace-membership/${request.workspaceSlug}/is-member/${request.userEmail}`, + headers + ) + return await parseResponse(response) + } + + async getMembers( + request: GetMembersRequest, + headers?: Record + ): Promise> { + let url = parsePaginationUrl( + `/api/workspace-membership/${request.workspaceSlug}/members`, + request + ) + const response = await this.apiClient.get(url, headers) + return await parseResponse(response) + } +} diff --git a/packages/api-client/src/index.ts b/packages/api-client/src/index.ts index 77192b35..6d1e7afc 100644 --- a/packages/api-client/src/index.ts +++ b/packages/api-client/src/index.ts @@ -6,6 +6,7 @@ import ProjectController from '@api-client/controllers/project' import VariableController from '@api-client/controllers/variable' import WorkspaceController from '@api-client/controllers/workspace' import WorkspaceRoleController from '@api-client/controllers/workspace-role' +import WorkspaceMembershipController from '@api-client/controllers/workspace-membership' export { EnvironmentController, @@ -15,5 +16,6 @@ export { ProjectController, VariableController, WorkspaceController, - WorkspaceRoleController + WorkspaceRoleController, + WorkspaceMembershipController } diff --git a/packages/api-client/src/types/workspace-membership.types.d.ts b/packages/api-client/src/types/workspace-membership.types.d.ts new file mode 100644 index 00000000..df22dff6 --- /dev/null +++ b/packages/api-client/src/types/workspace-membership.types.d.ts @@ -0,0 +1,145 @@ +import { Page } from '../../../../apps/cli/src/types/index.types' + +enum Authority { + CREATE_PROJECT, + READ_USERS, + ADD_USER, + REMOVE_USER, + UPDATE_USER_ROLE, + READ_WORKSPACE, + UPDATE_WORKSPACE, + DELETE_WORKSPACE, + CREATE_WORKSPACE_ROLE, + READ_WORKSPACE_ROLE, + UPDATE_WORKSPACE_ROLE, + DELETE_WORKSPACE_ROLE, + WORKSPACE_ADMIN, + READ_PROJECT, + UPDATE_PROJECT, + DELETE_PROJECT, + CREATE_SECRET, + READ_SECRET, + UPDATE_SECRET, + DELETE_SECRET, + CREATE_ENVIRONMENT, + READ_ENVIRONMENT, + UPDATE_ENVIRONMENT, + DELETE_ENVIRONMENT, + CREATE_VARIABLE, + READ_VARIABLE, + UPDATE_VARIABLE, + DELETE_VARIABLE, + CREATE_INTEGRATION, + READ_INTEGRATION, + UPDATE_INTEGRATION, + DELETE_INTEGRATION, + CREATE_WORKSPACE, + CREATE_API_KEY, + READ_API_KEY, + UPDATE_API_KEY, + DELETE_API_KEY, + UPDATE_PROFILE, + READ_SELF, + UPDATE_SELF, + READ_EVENT +} + +export interface CreateWorkspaceMember { + email: string + roleSlugs: string[] +} + +export interface TransferOwnershipRequest { + workspaceSlug: string + userEmail: string +} + +export interface TransferOwnershipResponse {} + +export interface InviteUsersRequest { + emails: string[] + workspaceSlug: string + members: CreateWorkspaceMember[] +} + +export interface InviteUsersResponse {} + +export interface RemoveUsersRequest { + workspaceSlug: string + userEmails: string[] +} + +export interface RemoveUsersResponse {} + +export interface UpdateMemberRoleRequest { + workspaceSlug: string + userEmail: string + roleSlugs: string[] +} + +export interface UpdateMemberRoleResponse {} + +export interface AcceptInvitationRequest { + workspaceSlug: string +} + +export interface AcceptInvitationResponse {} + +export interface DeclineInvitationRequest { + workspaceSlug: string +} + +export interface DeclineInvitationResponse {} + +export interface CancelInvitationRequest { + workspaceSlug: string + userEmail: string +} + +export interface CancelInvitationResponse {} + +export interface LeaveWorkspaceRequest { + workspaceSlug: string +} + +export interface LeaveWorkspaceResponse {} + +export interface IsMemberRequest { + workspaceSlug: string + userEmail: string +} + +export type IsMemberResponse = boolean + +export interface GetMembersRequest extends Page { + workspaceSlug: string + page?: number + limit?: number + sort?: string + order?: string + search?: string +} + +export interface GetMembersResponse + extends Page<{ + metadata: Record + id: string + user: { + id: string + email: string + name: string | null + } + roles: { + id: string + role: { + id: string + name: string + description: string | null + colorCode: string | null + authorities: Authority[] + projects: { + id: string + }[] + } + }[] + }> {} diff --git a/packages/api-client/tests/workspace-membership.spec.ts b/packages/api-client/tests/workspace-membership.spec.ts new file mode 100644 index 00000000..92e3cc28 --- /dev/null +++ b/packages/api-client/tests/workspace-membership.spec.ts @@ -0,0 +1,158 @@ +import WorkspaceMembershipController from '../src/controllers/workspace-membership' +import { APIClient } from '@api-client/core/client' + +describe('Workspace Membership Controller Tests', () => { + const backendUrl = process.env.BACKEND_URL + const apiClient = new APIClient(backendUrl) + const workspaceMembershipController = new WorkspaceMembershipController( + backendUrl + ) + const userEmail = 'testuser@example.com' + let workspaceSlug: string | null + + beforeAll(async () => { + // Create a workspace for the tests + const workspaceResponse = (await ( + await apiClient.post( + '/api/workspace', + { name: 'Test Workspace' }, + { 'x-e2e-user-email': userEmail } + ) + ).json()) as any + + workspaceSlug = workspaceResponse.slug + }) + + afterAll(async () => { + // Delete the workspace after tests + await apiClient.delete(`/api/workspace/${workspaceSlug}`, { + 'x-e2e-user-email': userEmail + }) + }) + + it('should transfer ownership', async () => { + const request = { workspaceSlug: workspaceSlug!, userEmail: userEmail } + const response = await workspaceMembershipController.transferOwnership( + request, + { + 'x-e2e-user-email': userEmail + } + ) + + expect(response).toBeTruthy() + }) + + it('should invite users', async () => { + const request = { + workspaceSlug: workspaceSlug!, + emails: ['invitee@example.com'], + members: [{ email: 'invitee@example.com', roleSlugs: ['member'] }] + } + const response = await workspaceMembershipController.inviteUsers(request, { + 'x-e2e-user-email': userEmail + }) + + expect(response).toBeTruthy() + }) + + it('should remove users', async () => { + const request = { + workspaceSlug: workspaceSlug!, + userEmails: ['invitee@example.com'] + } + const response = await workspaceMembershipController.removeUsers(request, { + 'x-e2e-user-email': userEmail + }) + + expect(response).toBeTruthy() + }) + + it('should update member roles', async () => { + const request = { + workspaceSlug: workspaceSlug!, + userEmail: 'invitee@example.com', + roleSlugs: ['admin'] + } + const response = await workspaceMembershipController.updateMemberRoles( + request, + { + 'x-e2e-user-email': userEmail + } + ) + + expect(response).toBeTruthy() + }) + + it('should accept an invitation', async () => { + const request = { workspaceSlug: workspaceSlug! } + const response = await workspaceMembershipController.acceptInvitation( + request, + { + 'x-e2e-user-email': userEmail + } + ) + + expect(response).toBeTruthy() + }) + + it('should decline an invitation', async () => { + const request = { workspaceSlug: workspaceSlug! } + const response = await workspaceMembershipController.declineInvitation( + request, + { + 'x-e2e-user-email': userEmail + } + ) + + expect(response).toBeTruthy() + }) + + it('should cancel an invitation', async () => { + const request = { + workspaceSlug: workspaceSlug!, + userEmail: 'invitee@example.com' + } + const response = await workspaceMembershipController.cancelInvitation( + request, + { + 'x-e2e-user-email': userEmail + } + ) + + expect(response).toBeTruthy() + }) + + it('should leave a workspace', async () => { + const request = { workspaceSlug: workspaceSlug! } + const response = await workspaceMembershipController.leaveWorkspace( + request, + { + 'x-e2e-user-email': userEmail + } + ) + + expect(response).toBeTruthy() + }) + + it('should check if a user is a member', async () => { + const request = { workspaceSlug: workspaceSlug!, userEmail: userEmail } + const response = await workspaceMembershipController.isMember(request, { + 'x-e2e-user-email': userEmail + }) + + expect(response).toBe(true) + }) + + it('should get a list of members', async () => { + const request = { + workspaceSlug: workspaceSlug!, + page: 0, + limit: 10 + } + const response = await workspaceMembershipController.getMembers(request, { + 'x-e2e-user-email': userEmail + }) + + expect(response.data.items.length).toBeGreaterThanOrEqual(0) + }) +})