Skip to content

Commit

Permalink
feat(api): Fetch total count of environments, secrets and variables i…
Browse files Browse the repository at this point in the history
…n project (keyshade-xyz#434)

Co-authored-by: codiumai-pr-agent-free[bot] <138128286+codiumai-pr-agent-free[bot]@users.noreply.github.com>
Co-authored-by: rajdip-b <agentR47@gmail.com>
  • Loading branch information
3 people authored and Kiranchaudhary537 committed Oct 13, 2024
1 parent 3c1674c commit 0d09900
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 3 deletions.
100 changes: 99 additions & 1 deletion apps/api/src/project/project.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe('Project Controller Tests', () => {

let user1: User, user2: User
let workspace1: Workspace, workspace2: Workspace
let project1: Project, project2: Project, project3: Project
let project1: Project, project2: Project, project3: Project, project4: Project

beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
Expand Down Expand Up @@ -142,6 +142,13 @@ describe('Project Controller Tests', () => {
storePrivateKey: true,
accessLevel: ProjectAccessLevel.GLOBAL
})) as Project

project4 = (await projectService.createProject(user2, workspace2.slug, {
name: 'Project4',
description:
'Project for testing if all environments,secrets and keys are being fetched or not',
storePrivateKey: true
})) as Project
})

afterEach(async () => {
Expand Down Expand Up @@ -519,6 +526,97 @@ describe('Project Controller Tests', () => {

expect(response.statusCode).toBe(401)
})

it('should fetch correct counts of environments, variables, and secrets for projects in a workspace', async () => {
// Add an environment to the project
const environment = (await environmentService.createEnvironment(
user2,
{
name: 'Dev'
},
project4.slug
)) as Environment

// Add two secrets
;(await secretService.createSecret(
user2,
{
name: 'API_KEY',
entries: [
{
value: 'some_key',
environmentSlug: environment.slug
}
]
},
project4.slug
)) as Secret
;(await secretService.createSecret(
user2,
{
name: 'DB_PASSWORD',
entries: [
{
value: 'password',
environmentSlug: environment.slug
}
]
},
project4.slug
)) as Secret

// Add two variables
;(await variableService.createVariable(
user2,
{
name: 'PORT',
entries: [
{
value: '8080',
environmentSlug: environment.slug
}
]
},
project4.slug
)) as Variable
;(await variableService.createVariable(
user2,
{
name: 'EXPIRY',
entries: [
{
value: '3600',
environmentSlug: environment.slug
}
]
},
project4.slug
)) as Variable

const response = await app.inject({
method: 'GET',
url: `/project/all/${workspace2.slug}?page=0&limit=10&search=Project4`,
headers: {
'x-e2e-user-email': user2.email
}
})

expect(response.statusCode).toBe(200)
expect(response.json().items.length).toEqual(1)

const project = response.json().items[0]
expect(project.totalEnvironmentsOfProject).toEqual(2)
expect(project.totalVariablesOfProject).toEqual(2)
expect(project.totalSecretsOfProject).toEqual(2)
// Verify project details
expect(project.name).toEqual('Project4')
expect(project.description).toEqual(
'Project for testing if all environments,secrets and keys are being fetched or not'
)
// Verify that sensitive data is not included
expect(project).not.toHaveProperty('privateKey')
expect(project).not.toHaveProperty('publicKey')
})
})

it('should create environments if provided', async () => {
Expand Down
86 changes: 84 additions & 2 deletions apps/api/src/project/service/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ export class ProjectService {
const workspaceId = workspace.id

//fetch projects with required properties
const items = (
const projects = (
await this.prisma.project.findMany({
skip: page * limit,
take: limitMaxItemsPerPage(limit),
Expand All @@ -801,14 +801,96 @@ export class ProjectService {
workspace: {
members: {
some: {
userId: user.id
userId: user.id,
roles: {
some: {
role: {
authorities: {
hasSome: [
Authority.WORKSPACE_ADMIN,
Authority.READ_PROJECT
]
}
}
}
}
}
}
}
}
})
).map((project) => excludeFields(project, 'privateKey', 'publicKey'))

const items = await Promise.all(
projects.map(async (project) => {
let totalEnvironmentsOfProject = 0
let totalVariablesOfProject = 0
let totalSecretsOfProject = 0
// When we later implement RBAC for environments, we would need to updated
// this code to only include environments like we do while fetching projects.

// What would be even better is, we should fetch environments directly. And then,
// accumulate the projects into a set of projects. And then, return that set along
// with the required data.
const allEnvs = await this.prisma.environment.findMany({
where: { projectId: project.id }
})

// This entire block will become invalid after RBAC for environments are implemented
const envPromises = allEnvs.map(async (env) => {
const hasRequiredPermission =
await this.authorityCheckerService.checkAuthorityOverEnvironment({
userId: user.id,
entity: { slug: env.slug },
authorities: [
Authority.READ_ENVIRONMENT,
Authority.READ_SECRET,
Authority.READ_VARIABLE
],
prisma: this.prisma
})
if (hasRequiredPermission) {
totalEnvironmentsOfProject += 1

const fetchSecretCount = this.prisma.secret.count({
where: {
projectId: project.id,
versions: { some: { environmentId: env.id } }
}
})

const fetchVariableCount = this.prisma.variable.count({
where: {
projectId: project.id,
versions: { some: { environmentId: env.id } }
}
})

return this.prisma.$transaction([
fetchSecretCount,
fetchVariableCount
])
}
return [0, 0]
})
const counts = await Promise.all(envPromises)
totalSecretsOfProject = counts.reduce(
(sum, [secretCount]) => sum + secretCount,
0
)
totalVariablesOfProject = counts.reduce(
(sum, [, variableCount]) => sum + variableCount,
0
)
return {
...project,
totalEnvironmentsOfProject,
totalVariablesOfProject,
totalSecretsOfProject
}
})
)

//calculate metadata
const totalCount = await this.prisma.project.count({
where: {
Expand Down

0 comments on commit 0d09900

Please sign in to comment.