diff --git a/backend/src/ee/services/access-approval-policy/access-approval-policy-dal.ts b/backend/src/ee/services/access-approval-policy/access-approval-policy-dal.ts index 77ae430c6..c224bd3ca 100644 --- a/backend/src/ee/services/access-approval-policy/access-approval-policy-dal.ts +++ b/backend/src/ee/services/access-approval-policy/access-approval-policy-dal.ts @@ -1,9 +1,9 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName, TAccessApprovalPolicies } from "@app/db/schemas"; +import { AccessApprovalPoliciesSchema, TableName, TAccessApprovalPolicies } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { buildFindFilter, mergeOneToManyRelation, ormify, selectAllTableCols, TFindFilter } from "@app/lib/knex"; +import { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex"; export type TAccessApprovalPolicyDALFactory = ReturnType; @@ -15,12 +15,12 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => { // eslint-disable-next-line .where(buildFindFilter(filter)) .join(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`) - .join( + .leftJoin( TableName.AccessApprovalPolicyApprover, `${TableName.AccessApprovalPolicy}.id`, `${TableName.AccessApprovalPolicyApprover}.policyId` ) - .select(tx.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover)) + .select(tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover)) .select(tx.ref("name").withSchema(TableName.Environment).as("envName")) .select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug")) .select(tx.ref("id").withSchema(TableName.Environment).as("envId")) @@ -35,18 +35,30 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => { const doc = await accessApprovalPolicyFindQuery(tx || db.replicaNode(), { [`${TableName.AccessApprovalPolicy}.id` as "id"]: id }); - const formatedDoc = mergeOneToManyRelation( - doc, - "id", - ({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({ - ...el, - envId, - environment: { id: envId, name, slug } + const formattedDoc = sqlNestRelationships({ + data: doc, + key: "id", + parentMapper: (data) => ({ + environment: { + id: data.envId, + name: data.envName, + slug: data.envSlug + }, + projectId: data.projectId, + ...AccessApprovalPoliciesSchema.parse(data) }), - ({ approverId }) => approverId, - "approvers" - ); - return formatedDoc?.[0]; + childrenMapper: [ + { + key: "approverUserId", + label: "userApprovers" as const, + mapper: ({ approverUserId }) => ({ + userId: approverUserId + }) + } + ] + }); + + return formattedDoc?.[0]; } catch (error) { throw new DatabaseError({ error, name: "FindById" }); } @@ -55,18 +67,32 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => { const find = async (filter: TFindFilter, tx?: Knex) => { try { const docs = await accessApprovalPolicyFindQuery(tx || db.replicaNode(), filter); - const formatedDoc = mergeOneToManyRelation( - docs, - "id", - ({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({ - ...el, - envId, - environment: { id: envId, name, slug } + + const formattedDocs = sqlNestRelationships({ + data: docs, + key: "id", + parentMapper: (data) => ({ + environment: { + id: data.envId, + name: data.envName, + slug: data.envSlug + }, + projectId: data.projectId, + ...AccessApprovalPoliciesSchema.parse(data) + // secretPath: data.secretPath || undefined, }), - ({ approverId }) => approverId, - "approvers" - ); - return formatedDoc.map((policy) => ({ ...policy, secretPath: policy.secretPath || undefined })); + childrenMapper: [ + { + key: "approverUserId", + label: "userApprovers" as const, + mapper: ({ approverUserId }) => ({ + userId: approverUserId + }) + } + ] + }); + + return formattedDocs; } catch (error) { throw new DatabaseError({ error, name: "Find" }); } diff --git a/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts b/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts index c91fbf970..9cff9a17f 100644 --- a/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts +++ b/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts @@ -34,8 +34,7 @@ export const accessApprovalPolicyServiceFactory = ({ accessApprovalPolicyApproverDAL, permissionService, projectEnvDAL, - projectDAL, - projectMembershipDAL + projectDAL }: TSecretApprovalPolicyServiceFactoryDep) => { const createAccessApprovalPolicy = async ({ name, @@ -45,7 +44,7 @@ export const accessApprovalPolicyServiceFactory = ({ secretPath, actorAuthMethod, approvals, - approvers, + approverUserIds, projectSlug, environment, enforcementLevel @@ -53,7 +52,7 @@ export const accessApprovalPolicyServiceFactory = ({ const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); if (!project) throw new BadRequestError({ message: "Project not found" }); - if (approvals > approvers.length) + if (approvals > approverUserIds.length) throw new BadRequestError({ message: "Approvals cannot be greater than approvers" }); const { permission } = await permissionService.getProjectPermission( @@ -70,15 +69,6 @@ export const accessApprovalPolicyServiceFactory = ({ const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id }); if (!env) throw new BadRequestError({ message: "Environment not found" }); - const secretApprovers = await projectMembershipDAL.find({ - projectId: project.id, - $in: { id: approvers } - }); - - if (secretApprovers.length !== approvers.length) { - throw new BadRequestError({ message: "Approver not found in project" }); - } - await verifyApprovers({ projectId: project.id, orgId: actorOrgId, @@ -86,7 +76,7 @@ export const accessApprovalPolicyServiceFactory = ({ secretPath, actorAuthMethod, permissionService, - userIds: secretApprovers.map((approver) => approver.userId) + userIds: approverUserIds }); const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => { @@ -101,8 +91,8 @@ export const accessApprovalPolicyServiceFactory = ({ tx ); await accessApprovalPolicyApproverDAL.insertMany( - secretApprovers.map(({ id }) => ({ - approverId: id, + approverUserIds.map((userId) => ({ + approverUserId: userId, policyId: doc.id })), tx @@ -138,7 +128,7 @@ export const accessApprovalPolicyServiceFactory = ({ const updateAccessApprovalPolicy = async ({ policyId, - approvers, + approverUserIds, secretPath, name, actorId, @@ -171,16 +161,7 @@ export const accessApprovalPolicyServiceFactory = ({ }, tx ); - if (approvers) { - // Find the workspace project memberships of the users passed in the approvers array - const secretApprovers = await projectMembershipDAL.find( - { - projectId: accessApprovalPolicy.projectId, - $in: { id: approvers } - }, - { tx } - ); - + if (approverUserIds) { await verifyApprovers({ projectId: accessApprovalPolicy.projectId, orgId: actorOrgId, @@ -188,15 +169,13 @@ export const accessApprovalPolicyServiceFactory = ({ secretPath: doc.secretPath!, actorAuthMethod, permissionService, - userIds: secretApprovers.map((approver) => approver.userId) + userIds: approverUserIds }); - if (secretApprovers.length !== approvers.length) - throw new BadRequestError({ message: "Approvals cannot be greater than approvers" }); await accessApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx); await accessApprovalPolicyApproverDAL.insertMany( - secretApprovers.map(({ id }) => ({ - approverId: id, + approverUserIds.map((userId) => ({ + approverUserId: userId, policyId: doc.id })), tx diff --git a/backend/src/ee/services/access-approval-policy/access-approval-policy-types.ts b/backend/src/ee/services/access-approval-policy/access-approval-policy-types.ts index fdb6fc8bb..83f4ffab6 100644 --- a/backend/src/ee/services/access-approval-policy/access-approval-policy-types.ts +++ b/backend/src/ee/services/access-approval-policy/access-approval-policy-types.ts @@ -17,7 +17,7 @@ export type TCreateAccessApprovalPolicy = { approvals: number; secretPath: string; environment: string; - approvers: string[]; + approverUserIds: string[]; projectSlug: string; name: string; enforcementLevel: EnforcementLevel; @@ -26,7 +26,7 @@ export type TCreateAccessApprovalPolicy = { export type TUpdateAccessApprovalPolicy = { policyId: string; approvals?: number; - approvers?: string[]; + approverUserIds?: string[]; secretPath?: string; name?: string; enforcementLevel?: EnforcementLevel;