diff --git a/apps/api/src/secret/dto/create.secret/create.secret.ts b/apps/api/src/secret/dto/create.secret/create.secret.ts index 3c8065a8..7c37aac5 100644 --- a/apps/api/src/secret/dto/create.secret/create.secret.ts +++ b/apps/api/src/secret/dto/create.secret/create.secret.ts @@ -1,4 +1,4 @@ -import { IsNumber, IsOptional, IsString, Length } from 'class-validator' +import { IsOptional, IsString, Length } from 'class-validator' export class CreateSecret { @IsString() @@ -12,8 +12,8 @@ export class CreateSecret { @Length(0, 100) note: string - @IsNumber() @IsOptional() + @IsString() environmentId: string @IsString() diff --git a/apps/api/src/variable/dto/create.variable/create.variable.ts b/apps/api/src/variable/dto/create.variable/create.variable.ts index 07f49b4b..54815465 100644 --- a/apps/api/src/variable/dto/create.variable/create.variable.ts +++ b/apps/api/src/variable/dto/create.variable/create.variable.ts @@ -1,4 +1,4 @@ -import { IsNumber, IsOptional, IsString, Length } from 'class-validator' +import { IsOptional, IsString, Length } from 'class-validator' export class CreateVariable { @IsString() @@ -12,7 +12,7 @@ export class CreateVariable { @Length(0, 100) note: string - @IsNumber() + @IsString() @IsOptional() environmentId: string } diff --git a/apps/api/src/workspace/controller/workspace.controller.ts b/apps/api/src/workspace/controller/workspace.controller.ts index 55c9098f..529f1cac 100644 --- a/apps/api/src/workspace/controller/workspace.controller.ts +++ b/apps/api/src/workspace/controller/workspace.controller.ts @@ -183,6 +183,15 @@ export class WorkspaceController { return this.workspaceService.getWorkspaceById(user, workspaceId) } + @Get(':workspaceId/export-data') + @RequiredApiKeyAuthorities(Authority.WORKSPACE_ADMIN) + async exportData( + @CurrentUser() user: User, + @Param('workspaceId') workspaceId: Workspace['id'] + ) { + return this.workspaceService.exportData(user, workspaceId) + } + @Get('/all') @RequiredApiKeyAuthorities(Authority.READ_WORKSPACE) async getAllWorkspacesOfUser( diff --git a/apps/api/src/workspace/service/workspace.service.ts b/apps/api/src/workspace/service/workspace.service.ts index f5d6a18b..9ca2a0e2 100644 --- a/apps/api/src/workspace/service/workspace.service.ts +++ b/apps/api/src/workspace/service/workspace.service.ts @@ -864,6 +864,89 @@ export class WorkspaceService { }) } + async exportData(user: User, workspaceId: Workspace['id']) { + const workspace = await getWorkspaceWithAuthority( + user.id, + workspaceId, + Authority.WORKSPACE_ADMIN, + this.prisma + ) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const data: any = {} + + data.name = workspace.name + data.description = workspace.description + + // Get all the roles of the workspace + data.workspaceRoles = await this.prisma.workspaceRole.findMany({ + where: { + workspaceId + }, + select: { + name: true, + description: true, + colorCode: true, + hasAdminAuthority: true, + authorities: true, + projects: { + select: { + id: true + } + } + } + }) + + // Get all projects, environments, variables and secrets of the workspace + data.projects = await this.prisma.project.findMany({ + where: { + workspaceId + }, + select: { + name: true, + description: true, + publicKey: true, + privateKey: true, + storePrivateKey: true, + isPublic: true, + environments: { + select: { + name: true, + description: true, + isDefault: true, + secrets: { + select: { + name: true, + rotateAt: true, + note: true, + versions: { + select: { + value: true, + version: true + } + } + } + }, + variables: { + select: { + name: true, + note: true, + versions: { + select: { + value: true, + version: true + } + } + } + } + } + } + } + }) + + return data + } + private async existsByName( name: string, userId: User['id'] diff --git a/apps/api/src/workspace/workspace.e2e.spec.ts b/apps/api/src/workspace/workspace.e2e.spec.ts index 22b5e469..ca0f83c4 100644 --- a/apps/api/src/workspace/workspace.e2e.spec.ts +++ b/apps/api/src/workspace/workspace.e2e.spec.ts @@ -1131,6 +1131,59 @@ describe('Workspace Controller Tests', () => { expect(response.json()).toEqual(expect.arrayContaining([event])) }) + it('should not be able to export data of a non-existing workspace', async () => { + const response = await app.inject({ + method: 'GET', + headers: { + 'x-e2e-user-email': user1.email + }, + url: `/workspace/abc/export-data` + }) + + expect(response.statusCode).toBe(404) + expect(response.json()).toEqual({ + statusCode: 404, + error: 'Not Found', + message: `Workspace with id abc not found` + }) + }) + + it('should not be able to export data of a workspace it is not a member of', async () => { + const response = await app.inject({ + method: 'GET', + headers: { + 'x-e2e-user-email': user1.email + }, + url: `/workspace/${workspace1.id}/export-data` + }) + + expect(response.statusCode).toBe(401) + expect(response.json()).toEqual({ + statusCode: 401, + error: 'Unauthorized', + message: `User ${user1.id} does not have the required authorities to perform the action` + }) + }) + + it('should be able to export data of the workspace', async () => { + const response = await app.inject({ + method: 'GET', + headers: { + 'x-e2e-user-email': user2.email + }, + url: `/workspace/${workspace1.id}/export-data` + }) + + expect(response.statusCode).toBe(200) + + const body = response.json() + + expect(body.name).toEqual(workspace1.name) + expect(body.description).toEqual(workspace1.description) + expect(body.workspaceRoles).toBeInstanceOf(Array) + expect(body.projects).toBeInstanceOf(Array) + }) + it('should be able to delete the workspace', async () => { const response = await app.inject({ method: 'DELETE',