From 7bf246190e5fb03a514d6e34ae090f480865ab14 Mon Sep 17 00:00:00 2001 From: Ritika Mandal Date: Sat, 8 Jun 2024 17:03:12 +0530 Subject: [PATCH 1/4] Update return type while fetching secrets and variables --- apps/api/src/secret/secret.e2e.spec.ts | 4 +- apps/api/src/secret/service/secret.service.ts | 54 +++++++++- .../src/variable/service/variable.service.ts | 102 +++++++++++------- 3 files changed, 120 insertions(+), 40 deletions(-) diff --git a/apps/api/src/secret/secret.e2e.spec.ts b/apps/api/src/secret/secret.e2e.spec.ts index 4232eaae..9de4500b 100644 --- a/apps/api/src/secret/secret.e2e.spec.ts +++ b/apps/api/src/secret/secret.e2e.spec.ts @@ -840,10 +840,8 @@ describe('Secret Controller Tests', () => { expect(response.statusCode).toBe(200) expect(response.json().length).toBe(1) - const secret = response.json()[0] - - expect(secret.versions[0].value).toEqual('Secret 1 value') + expect(secret.secrets[0].versions[0].value).toEqual('Secret 1 value') }) it('should not be able to fetch all secrets decrypted if the project does not store the private key', async () => { diff --git a/apps/api/src/secret/service/secret.service.ts b/apps/api/src/secret/service/secret.service.ts index b28d8754..31496b68 100644 --- a/apps/api/src/secret/service/secret.service.ts +++ b/apps/api/src/secret/service/secret.service.ts @@ -503,7 +503,59 @@ export class SecretService { } } - return secrets + // Group secrets by environment + const secretsByEnvironment = await secrets.reduce( + async (accPromise, secret) => { + const acc = await accPromise + const environmentName = await this.getEnvironmentName( + secret.environmentId + ) + + if (!acc[secret.environmentId]) { + acc[secret.environmentId] = { + environmentName: environmentName, + environmentId: secret.environmentId, + secrets: [] + } + } + + acc[secret.environmentId].secrets.push(secret) + return acc + }, + Promise.resolve({}) + ) + + // Convert the result to an array + const clusteredSecrets = Object.values(secretsByEnvironment) + + console.log(clusteredSecrets) + + return clusteredSecrets + } + + private async getEnvironmentName( + environmentId: string + ): Promise { + try { + // Fetch the environment using its unique ID + const environment = await this.prisma.environment.findUnique({ + where: { + id: environmentId + } + }) + + // Check if the environment exists + if (environment) { + // Access and return the name property + return environment.name + } else { + console.log('Environment not found') + return null + } + } catch (error) { + console.error('Error fetching environment:', error) + return null + } } private async secretExists( diff --git a/apps/api/src/variable/service/variable.service.ts b/apps/api/src/variable/service/variable.service.ts index c0c3c517..67cc6eb3 100644 --- a/apps/api/src/variable/service/variable.service.ts +++ b/apps/api/src/variable/service/variable.service.ts @@ -413,49 +413,79 @@ export class VariableService { sort: string, order: string, search: string - ) { - // Check if the user has the required authorities in the project - await this.authorityCheckerService.checkAuthorityOverProject({ - userId: user.id, - entity: { id: projectId }, - authority: Authority.READ_VARIABLE, - prisma: this.prisma - }) + ): Promise< + { environmentName: string; environmentId: string; variables: any[] }[] + > { + try { + // Check if the user has the required authorities in the project + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.READ_VARIABLE, + prisma: this.prisma + }) - return await this.prisma.variable.findMany({ - where: { - projectId, - pendingCreation: false, - name: { - contains: search - } - }, - include: { - versions: { - orderBy: { - version: 'desc' - }, - take: 1 + const variables = await this.prisma.variable.findMany({ + where: { + projectId, + pendingCreation: false, + name: { + contains: search + } }, - lastUpdatedBy: { - select: { - id: true, - name: true + include: { + versions: { + orderBy: { + version: 'desc' + }, + take: 1 + }, + lastUpdatedBy: { + select: { + id: true, + name: true + } + }, + environment: { + select: { + id: true, + name: true + } } }, - environment: { - select: { - id: true, - name: true + skip: page * limit, + take: limit, + orderBy: { + [sort]: order + } + }) + + // Group variables by environment + const variablesByEnvironment: { + [key: string]: { + environmentName: string + environmentId: string + variables: any[] + } + } = {} + for (const variable of variables) { + const { id, name } = variable.environment + if (!variablesByEnvironment[id]) { + variablesByEnvironment[id] = { + environmentName: name, + environmentId: id, + variables: [] } } - }, - skip: page * limit, - take: limit, - orderBy: { - [sort]: order + variablesByEnvironment[id].variables.push(variable) } - }) + + // Convert the object to an array and return + return Object.values(variablesByEnvironment) + } catch (error) { + console.error('Error fetching variables:', error) + throw error // Or handle the error as per your application's requirement + } } private async variableExists( From 426acf8f8c0499bca47b3fe933f8ef4f5b203df9 Mon Sep 17 00:00:00 2001 From: Ritika Mandal Date: Sun, 9 Jun 2024 12:57:41 +0530 Subject: [PATCH 2/4] Code update with changes requested --- apps/api/src/secret/secret.e2e.spec.ts | 5 +- apps/api/src/secret/secret.types.ts | 6 + apps/api/src/secret/service/secret.service.ts | 63 +++------ .../src/variable/service/variable.service.ts | 122 ++++++++---------- apps/api/src/variable/variable.e2e.spec.ts | 3 + apps/api/src/variable/variable.types.ts | 5 + 6 files changed, 90 insertions(+), 114 deletions(-) diff --git a/apps/api/src/secret/secret.e2e.spec.ts b/apps/api/src/secret/secret.e2e.spec.ts index 9de4500b..63fc8d19 100644 --- a/apps/api/src/secret/secret.e2e.spec.ts +++ b/apps/api/src/secret/secret.e2e.spec.ts @@ -840,8 +840,9 @@ describe('Secret Controller Tests', () => { expect(response.statusCode).toBe(200) expect(response.json().length).toBe(1) - const secret = response.json()[0] - expect(secret.secrets[0].versions[0].value).toEqual('Secret 1 value') + const envsecret = response.json()[0] + expect(envsecret.environment).toHaveProperty('id') + expect(envsecret.environment).toHaveProperty('name') }) it('should not be able to fetch all secrets decrypted if the project does not store the private key', async () => { diff --git a/apps/api/src/secret/secret.types.ts b/apps/api/src/secret/secret.types.ts index 89f5c9e7..4fdb0e85 100644 --- a/apps/api/src/secret/secret.types.ts +++ b/apps/api/src/secret/secret.types.ts @@ -16,4 +16,10 @@ export interface SecretWithEnvironment extends Secret { environment: Environment } +export type SecretsByEnvironment = { + environment: { id: string; name: string } + secrets: any[] +} export type SecretWithProjectAndVersion = SecretWithProject & SecretWithVersion +export type SecretWithVersionAndEnvironment = SecretWithVersion & + SecretWithEnvironment diff --git a/apps/api/src/secret/service/secret.service.ts b/apps/api/src/secret/service/secret.service.ts index 31496b68..f330146f 100644 --- a/apps/api/src/secret/service/secret.service.ts +++ b/apps/api/src/secret/service/secret.service.ts @@ -26,9 +26,12 @@ import { PrismaService } from '../../prisma/prisma.service' import { addHoursToDate } from '../../common/add-hours-to-date' import { encrypt } from '../../common/encrypt' import { + SecretWithEnvironment, SecretWithProject, SecretWithProjectAndVersion, - SecretWithVersion + SecretWithVersion, + SecretWithVersionAndEnvironment, + SecretsByEnvironment } from '../secret.types' import createEvent from '../../common/create-event' import getDefaultEnvironmentOfProject from '../../common/get-default-project-environment' @@ -488,7 +491,7 @@ export class SecretService { orderBy: { [sort]: order } - })) as SecretWithVersion[] + })) as unknown as SecretWithVersionAndEnvironment[] if (decryptValue) { for (const secret of secrets) { @@ -502,62 +505,28 @@ export class SecretService { } } } - // Group secrets by environment - const secretsByEnvironment = await secrets.reduce( - async (accPromise, secret) => { - const acc = await accPromise - const environmentName = await this.getEnvironmentName( - secret.environmentId - ) - - if (!acc[secret.environmentId]) { - acc[secret.environmentId] = { - environmentName: environmentName, - environmentId: secret.environmentId, + const secretsByEnvironment = secrets.reduce((acc, secret) => { + const { environment } = secret + if (environment) { + const { id, name } = environment + if (!acc[id]) { + acc[id] = { + environment: { id, name }, secrets: [] } } - - acc[secret.environmentId].secrets.push(secret) - return acc - }, - Promise.resolve({}) - ) + acc[id].secrets.push(secret) + } + return acc + }, {} as SecretsByEnvironment) // Convert the result to an array const clusteredSecrets = Object.values(secretsByEnvironment) - console.log(clusteredSecrets) - return clusteredSecrets } - private async getEnvironmentName( - environmentId: string - ): Promise { - try { - // Fetch the environment using its unique ID - const environment = await this.prisma.environment.findUnique({ - where: { - id: environmentId - } - }) - - // Check if the environment exists - if (environment) { - // Access and return the name property - return environment.name - } else { - console.log('Environment not found') - return null - } - } catch (error) { - console.error('Error fetching environment:', error) - return null - } - } - private async secretExists( secretName: Secret['name'], environmentId: Environment['id'] diff --git a/apps/api/src/variable/service/variable.service.ts b/apps/api/src/variable/service/variable.service.ts index 67cc6eb3..b8585a1a 100644 --- a/apps/api/src/variable/service/variable.service.ts +++ b/apps/api/src/variable/service/variable.service.ts @@ -29,7 +29,8 @@ import createApproval from '../../common/create-approval' import { UpdateVariableMetadata } from '../../approval/approval.types' import { VariableWithProject, - VariableWithProjectAndVersion + VariableWithProjectAndVersion, + VariablesByEnvironment } from '../variable.types' import { RedisClientType } from 'redis' import { REDIS_CLIENT } from '../../provider/redis.provider' @@ -413,79 +414,70 @@ export class VariableService { sort: string, order: string, search: string - ): Promise< - { environmentName: string; environmentId: string; variables: any[] }[] - > { - try { - // Check if the user has the required authorities in the project - await this.authorityCheckerService.checkAuthorityOverProject({ - userId: user.id, - entity: { id: projectId }, - authority: Authority.READ_VARIABLE, - prisma: this.prisma - }) + ): Promise { + // Check if the user has the required authorities in the project + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.READ_VARIABLE, + prisma: this.prisma + }) - const variables = await this.prisma.variable.findMany({ - where: { - projectId, - pendingCreation: false, - name: { - contains: search - } - }, - include: { - versions: { - orderBy: { - version: 'desc' - }, - take: 1 - }, - lastUpdatedBy: { - select: { - id: true, - name: true - } + const variables = await this.prisma.variable.findMany({ + where: { + projectId, + pendingCreation: false, + name: { + contains: search + } + }, + include: { + versions: { + orderBy: { + version: 'desc' }, - environment: { - select: { - id: true, - name: true - } + take: 1 + }, + lastUpdatedBy: { + select: { + id: true, + name: true } }, - skip: page * limit, - take: limit, - orderBy: { - [sort]: order - } - }) - - // Group variables by environment - const variablesByEnvironment: { - [key: string]: { - environmentName: string - environmentId: string - variables: any[] - } - } = {} - for (const variable of variables) { - const { id, name } = variable.environment - if (!variablesByEnvironment[id]) { - variablesByEnvironment[id] = { - environmentName: name, - environmentId: id, - variables: [] + environment: { + select: { + id: true, + name: true } } - variablesByEnvironment[id].variables.push(variable) + }, + skip: page * limit, + take: limit, + orderBy: { + [sort]: order } + }) - // Convert the object to an array and return - return Object.values(variablesByEnvironment) - } catch (error) { - console.error('Error fetching variables:', error) - throw error // Or handle the error as per your application's requirement + // Group variables by environment + const variablesByEnvironment: { + [key: string]: { + environment: { id: string; name: string } + variables: any[] + } + } = {} + for (const variable of variables) { + const { id, name } = variable.environment + if (!variablesByEnvironment[id]) { + variablesByEnvironment[id] = { + environment: { id, name }, + variables: [] + } + } + variablesByEnvironment[id].variables.push(variable) } + + // Convert the object to an array and return + return Object.values(variablesByEnvironment) } private async variableExists( diff --git a/apps/api/src/variable/variable.e2e.spec.ts b/apps/api/src/variable/variable.e2e.spec.ts index 1257fdae..ee62956c 100644 --- a/apps/api/src/variable/variable.e2e.spec.ts +++ b/apps/api/src/variable/variable.e2e.spec.ts @@ -774,6 +774,9 @@ describe('Variable Controller Tests', () => { expect(response.statusCode).toBe(200) expect(response.json().length).toBe(1) + const envvariable = response.json()[0] + expect(envvariable.environment).toHaveProperty('id') + expect(envvariable.environment).toHaveProperty('name') }) it('should not be able to fetch all variables if the user has no access to the project', async () => { diff --git a/apps/api/src/variable/variable.types.ts b/apps/api/src/variable/variable.types.ts index ebef53af..075fd0c3 100644 --- a/apps/api/src/variable/variable.types.ts +++ b/apps/api/src/variable/variable.types.ts @@ -16,5 +16,10 @@ export interface VariableWithEnvironment extends Variable { environment: Environment } +export type VariablesByEnvironment = { + environment: { id: string; name: string } + variables: any[] +} + export type VariableWithProjectAndVersion = VariableWithProject & VariableWithVersion From 5a1c9b36c118060a4eec0841899854f2a8bae51f Mon Sep 17 00:00:00 2001 From: Ritika Mandal Date: Sun, 9 Jun 2024 13:38:40 +0530 Subject: [PATCH 3/4] Code updated --- apps/api/src/secret/secret.e2e.spec.ts | 6 +- apps/api/src/secret/secret.types.ts | 4 -- apps/api/src/secret/service/secret.service.ts | 61 ++++++++++--------- .../src/variable/service/variable.service.ts | 10 ++- apps/api/src/variable/variable.e2e.spec.ts | 6 +- apps/api/src/variable/variable.types.ts | 5 -- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/apps/api/src/secret/secret.e2e.spec.ts b/apps/api/src/secret/secret.e2e.spec.ts index 63fc8d19..efa15bb6 100644 --- a/apps/api/src/secret/secret.e2e.spec.ts +++ b/apps/api/src/secret/secret.e2e.spec.ts @@ -840,9 +840,9 @@ describe('Secret Controller Tests', () => { expect(response.statusCode).toBe(200) expect(response.json().length).toBe(1) - const envsecret = response.json()[0] - expect(envsecret.environment).toHaveProperty('id') - expect(envsecret.environment).toHaveProperty('name') + const envSecret = response.json()[0] + expect(envSecret.environment).toHaveProperty('id') + expect(envSecret.environment).toHaveProperty('name') }) it('should not be able to fetch all secrets decrypted if the project does not store the private key', async () => { diff --git a/apps/api/src/secret/secret.types.ts b/apps/api/src/secret/secret.types.ts index 4fdb0e85..322aed65 100644 --- a/apps/api/src/secret/secret.types.ts +++ b/apps/api/src/secret/secret.types.ts @@ -16,10 +16,6 @@ export interface SecretWithEnvironment extends Secret { environment: Environment } -export type SecretsByEnvironment = { - environment: { id: string; name: string } - secrets: any[] -} export type SecretWithProjectAndVersion = SecretWithProject & SecretWithVersion export type SecretWithVersionAndEnvironment = SecretWithVersion & SecretWithEnvironment diff --git a/apps/api/src/secret/service/secret.service.ts b/apps/api/src/secret/service/secret.service.ts index f330146f..bd720065 100644 --- a/apps/api/src/secret/service/secret.service.ts +++ b/apps/api/src/secret/service/secret.service.ts @@ -26,12 +26,9 @@ import { PrismaService } from '../../prisma/prisma.service' import { addHoursToDate } from '../../common/add-hours-to-date' import { encrypt } from '../../common/encrypt' import { - SecretWithEnvironment, SecretWithProject, SecretWithProjectAndVersion, - SecretWithVersion, - SecretWithVersionAndEnvironment, - SecretsByEnvironment + SecretWithVersionAndEnvironment } from '../secret.types' import createEvent from '../../common/create-event' import getDefaultEnvironmentOfProject from '../../common/get-default-project-environment' @@ -433,7 +430,12 @@ export class SecretService { sort: string, order: string, search: string - ) { + ): Promise< + { + environment: { id: string; name: string } + secrets: any[] + }[] + > { // Fetch the project const project = await this.authorityCheckerService.checkAuthorityOverProject({ @@ -493,38 +495,41 @@ export class SecretService { } })) as unknown as SecretWithVersionAndEnvironment[] - if (decryptValue) { - for (const secret of secrets) { - // Decrypt the secret value - for (let i = 0; i < secret.versions.length; i++) { + for (const secret of secrets) { + // Decrypt the secret value + for (let i = 0; i < secret.versions.length; i++) { + const version = secret.versions[i] + // Optionally decrypt secret value if decryptValue is true + if (decryptValue) { const decryptedValue = await decrypt( project.privateKey, - secret.versions[i].value + version.value ) - secret.versions[i].value = decryptedValue + version.value = decryptedValue } } } - // Group secrets by environment - const secretsByEnvironment = secrets.reduce((acc, secret) => { - const { environment } = secret - if (environment) { - const { id, name } = environment - if (!acc[id]) { - acc[id] = { - environment: { id, name }, - secrets: [] - } + + // Group variables by environment + const secretsByEnvironment: { + [key: string]: { + environment: { id: string; name: string } + secrets: any[] + } + } = {} + for (const secret of secrets) { + const { id, name } = secret.environment + if (!secretsByEnvironment[id]) { + secretsByEnvironment[id] = { + environment: { id, name }, + secrets: [] } - acc[id].secrets.push(secret) } - return acc - }, {} as SecretsByEnvironment) - - // Convert the result to an array - const clusteredSecrets = Object.values(secretsByEnvironment) + secretsByEnvironment[id].secrets.push(secret) + } - return clusteredSecrets + // Convert the object to an array and return + return Object.values(secretsByEnvironment) } private async secretExists( diff --git a/apps/api/src/variable/service/variable.service.ts b/apps/api/src/variable/service/variable.service.ts index b8585a1a..b115bb6f 100644 --- a/apps/api/src/variable/service/variable.service.ts +++ b/apps/api/src/variable/service/variable.service.ts @@ -29,8 +29,7 @@ import createApproval from '../../common/create-approval' import { UpdateVariableMetadata } from '../../approval/approval.types' import { VariableWithProject, - VariableWithProjectAndVersion, - VariablesByEnvironment + VariableWithProjectAndVersion } from '../variable.types' import { RedisClientType } from 'redis' import { REDIS_CLIENT } from '../../provider/redis.provider' @@ -414,7 +413,12 @@ export class VariableService { sort: string, order: string, search: string - ): Promise { + ): Promise< + { + environment: { id: string; name: string } + variables: any[] + }[] + > { // Check if the user has the required authorities in the project await this.authorityCheckerService.checkAuthorityOverProject({ userId: user.id, diff --git a/apps/api/src/variable/variable.e2e.spec.ts b/apps/api/src/variable/variable.e2e.spec.ts index ee62956c..4a05ae13 100644 --- a/apps/api/src/variable/variable.e2e.spec.ts +++ b/apps/api/src/variable/variable.e2e.spec.ts @@ -774,9 +774,9 @@ describe('Variable Controller Tests', () => { expect(response.statusCode).toBe(200) expect(response.json().length).toBe(1) - const envvariable = response.json()[0] - expect(envvariable.environment).toHaveProperty('id') - expect(envvariable.environment).toHaveProperty('name') + const envVariable = response.json()[0] + expect(envVariable.environment).toHaveProperty('id') + expect(envVariable.environment).toHaveProperty('name') }) it('should not be able to fetch all variables if the user has no access to the project', async () => { diff --git a/apps/api/src/variable/variable.types.ts b/apps/api/src/variable/variable.types.ts index 075fd0c3..ebef53af 100644 --- a/apps/api/src/variable/variable.types.ts +++ b/apps/api/src/variable/variable.types.ts @@ -16,10 +16,5 @@ export interface VariableWithEnvironment extends Variable { environment: Environment } -export type VariablesByEnvironment = { - environment: { id: string; name: string } - variables: any[] -} - export type VariableWithProjectAndVersion = VariableWithProject & VariableWithVersion From 3de8cd6456128f3611b33dd802ae33fdba04ec46 Mon Sep 17 00:00:00 2001 From: Ritika Mandal Date: Sun, 9 Jun 2024 13:54:46 +0530 Subject: [PATCH 4/4] Code Refactored --- apps/api/src/secret/service/secret.service.ts | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/api/src/secret/service/secret.service.ts b/apps/api/src/secret/service/secret.service.ts index bd720065..9f121353 100644 --- a/apps/api/src/secret/service/secret.service.ts +++ b/apps/api/src/secret/service/secret.service.ts @@ -460,7 +460,7 @@ export class SecretService { ) } - const secrets = (await this.prisma.secret.findMany({ + const secrets = await this.prisma.secret.findMany({ where: { projectId, pendingCreation: false, @@ -493,7 +493,15 @@ export class SecretService { orderBy: { [sort]: order } - })) as unknown as SecretWithVersionAndEnvironment[] + }) + + // Group variables by environment + const secretsByEnvironment: { + [key: string]: { + environment: { id: string; name: string } + secrets: any[] + } + } = {} for (const secret of secrets) { // Decrypt the secret value @@ -508,16 +516,7 @@ export class SecretService { version.value = decryptedValue } } - } - // Group variables by environment - const secretsByEnvironment: { - [key: string]: { - environment: { id: string; name: string } - secrets: any[] - } - } = {} - for (const secret of secrets) { const { id, name } = secret.environment if (!secretsByEnvironment[id]) { secretsByEnvironment[id] = {