From de2b602dcd5bdab104d910b12761a6ec778103b8 Mon Sep 17 00:00:00 2001 From: Anudeep <94232161+anudeeps352@users.noreply.github.com> Date: Mon, 29 Jul 2024 19:17:32 +0530 Subject: [PATCH] feat(api): Create endpoint for fetching all revisions of a secret (#303) --- .../secret/controller/secret.controller.ts | 20 ++++ apps/api/src/secret/secret.e2e.spec.ts | 104 ++++++++++++++++++ apps/api/src/secret/service/secret.service.ts | 39 +++++++ 3 files changed, 163 insertions(+) diff --git a/apps/api/src/secret/controller/secret.controller.ts b/apps/api/src/secret/controller/secret.controller.ts index 59f36993..16274f11 100644 --- a/apps/api/src/secret/controller/secret.controller.ts +++ b/apps/api/src/secret/controller/secret.controller.ts @@ -101,4 +101,24 @@ export class SecretController { environmentId ) } + + @Get(':secretId/revisions/:environmentId') + @RequiredApiKeyAuthorities(Authority.READ_SECRET) + async getRevisionsOfSecret( + @CurrentUser() user: User, + @Param('secretId') secretId: string, + @Param('environmentId') environmentId: string, + @Query('page') page: number = 0, + @Query('limit') limit: number = 10, + @Query('order') order: string = 'desc' + ) { + return await this.secretService.getRevisionsOfSecret( + user, + secretId, + environmentId, + page, + limit, + order + ) + } } diff --git a/apps/api/src/secret/secret.e2e.spec.ts b/apps/api/src/secret/secret.e2e.spec.ts index 4efd828b..96a8eff3 100644 --- a/apps/api/src/secret/secret.e2e.spec.ts +++ b/apps/api/src/secret/secret.e2e.spec.ts @@ -920,4 +920,108 @@ describe('Secret Controller Tests', () => { expect(event.workspaceId).toBe(workspace1.id) expect(event.itemId).toBe(secret1.id) }) + + //revisions test + it('should be able to fetch all revisions of secrets', async () => { + // create two more entries,totalling three versions + // checks if its able to fetch multiple revisions + await secretService.updateSecret(user1, secret1.id, { + entries: [ + { + value: 'Updated Secret 1 value', + environmentId: environment1.id + } + ] + }) + + await secretService.updateSecret(user1, secret1.id, { + entries: [ + { + value: 'Updated Secret 1 value 2', + environmentId: environment1.id + } + ] + }) + + const response = await app.inject({ + method: 'GET', + url: `/secret/${secret1.id}/revisions/${environment1.id}`, + headers: { + 'x-e2e-user-email': user1.email + } + }) + + expect(response.statusCode).toBe(200) + expect(response.json().length).toBe(3) + }) + + it('should return [] if the secret has no revision', async () => { + //returns [] if secret has no revision + await prisma.secretVersion.deleteMany({ + where: { + secretId: secret1.id + } + }) + + const response = await app.inject({ + method: 'GET', + url: `/secret/${secret1.id}/revisions/${environment1.id}`, + headers: { + 'x-e2e-user-email': user1.email + } + }) + + expect(response.statusCode).toBe(200) + expect(response.json().length).toBe(0) + }) + + it('should return error if secret doesnt exist', async () => { + //return error if secret doesnt exist + const secretid = 'nonexistentsecret' + const response = await app.inject({ + method: 'GET', + url: `/secret/${secretid}/revisions/${environment1.id}`, + headers: { + 'x-e2e-user-email': user1.email + } + }) + + expect(response.statusCode).toBe(404) + expect(response.json().message).toEqual( + `Secret with id ${secretid} not found` + ) + }) + + it('should return error if environment doesnt exist', async () => { + //return error if environment doesnt exist + const environmentid = 'nonexistentenv' + const response = await app.inject({ + method: 'GET', + url: `/secret/${secret1.id}/revisions/${environmentid}`, + headers: { + 'x-e2e-user-email': user1.email + } + }) + + expect(response.statusCode).toBe(404) + expect(response.json().message).toEqual( + `Environment with id ${environmentid} not found` + ) + }) + + it('returns error if secret isnt accessible', async () => { + //return error if user has no access to secret + const response = await app.inject({ + method: 'GET', + url: `/secret/${secret1.id}/revisions/${environment1.id}`, + headers: { + 'x-e2e-user-email': user2.email + } + }) + + expect(response.statusCode).toBe(401) + expect(response.json().message).toEqual( + `User ${user2.id} does not have the required authorities` + ) + }) }) diff --git a/apps/api/src/secret/service/secret.service.ts b/apps/api/src/secret/service/secret.service.ts index 255d0960..156c5238 100644 --- a/apps/api/src/secret/service/secret.service.ts +++ b/apps/api/src/secret/service/secret.service.ts @@ -492,6 +492,45 @@ export class SecretService { return response } + async getRevisionsOfSecret( + user: User, + secretId: Secret['id'], + environmentId: Environment['id'], + page: number, + limit: number, + order: string + ) { + // assign order to variable dynamically + const sortOrder = order === 'asc' ? 'asc' : 'desc' + //check access to secret + await this.authorityCheckerService.checkAuthorityOverSecret({ + userId: user.id, + entity: { id: secretId }, + authority: Authority.READ_SECRET, + prisma: this.prisma + }) + + await this.authorityCheckerService.checkAuthorityOverEnvironment({ + userId: user.id, + entity: { id: environmentId }, + authority: Authority.READ_ENVIRONMENT, + prisma: this.prisma + }) + + // get the revisions + const revisions = await this.prisma.secretVersion.findMany({ + where: { + secretId: secretId, + environmentId: environmentId + }, + skip: page * limit, + take: limit, + orderBy: { + version: sortOrder + } + }) + return revisions + } async getAllSecretsOfProject( user: User,