From 558a809b4cc35671e1956afc7e89f3169e941c9f Mon Sep 17 00:00:00 2001 From: = Date: Sat, 19 Oct 2024 21:14:15 +0530 Subject: [PATCH] feat: made v2 project role router use projectid instead of projectslug --- .../src/ee/routes/v1/project-role-router.ts | 18 ++- .../src/ee/routes/v2/project-role-router.ts | 76 ++++-------- backend/src/lib/api-docs/constants.ts | 7 +- .../project-role/project-role-service.ts | 108 +++++++++--------- .../project-role/project-role-types.ts | 21 +++- frontend/src/hooks/api/roles/queries.tsx | 2 +- 6 files changed, 112 insertions(+), 120 deletions(-) diff --git a/backend/src/ee/routes/v1/project-role-router.ts b/backend/src/ee/routes/v1/project-role-router.ts index 8e697c30d..b79ced69b 100644 --- a/backend/src/ee/routes/v1/project-role-router.ts +++ b/backend/src/ee/routes/v1/project-role-router.ts @@ -12,6 +12,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { SanitizedRoleSchemaV1 } from "@app/server/routes/sanitizedSchemas"; import { AuthMode } from "@app/services/auth/auth-type"; +import { ProjectRoleServiceIdentifierType } from "@app/services/project-role/project-role-types"; export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { server.route({ @@ -61,7 +62,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type, - projectSlug: req.params.projectSlug, + filter: { + type: ProjectRoleServiceIdentifierType.SLUG, + projectSlug: req.params.projectSlug + }, data: { ...req.body, permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions))) @@ -121,7 +125,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type, - projectSlug: req.params.projectSlug, roleId: req.params.roleId, data: { ...req.body, @@ -164,7 +167,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type, - projectSlug: req.params.projectSlug, roleId: req.params.roleId }); return { role }; @@ -200,7 +202,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type, - projectSlug: req.params.projectSlug + filter: { + type: ProjectRoleServiceIdentifierType.SLUG, + projectSlug: req.params.projectSlug + } }); return { roles }; } @@ -230,7 +235,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type, - projectSlug: req.params.projectSlug, + filter: { + type: ProjectRoleServiceIdentifierType.SLUG, + projectSlug: req.params.projectSlug + }, roleSlug: req.params.slug }); return { role }; diff --git a/backend/src/ee/routes/v2/project-role-router.ts b/backend/src/ee/routes/v2/project-role-router.ts index 86d64141d..3b38fb21d 100644 --- a/backend/src/ee/routes/v2/project-role-router.ts +++ b/backend/src/ee/routes/v2/project-role-router.ts @@ -2,18 +2,19 @@ import { packRules } from "@casl/ability/extra"; import slugify from "@sindresorhus/slugify"; import { z } from "zod"; -import { ProjectMembershipRole, ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas"; +import { ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas"; import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission"; import { PROJECT_ROLE } from "@app/lib/api-docs"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas"; import { AuthMode } from "@app/services/auth/auth-type"; +import { ProjectRoleServiceIdentifierType } from "@app/services/project-role/project-role-types"; export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { server.route({ method: "POST", - url: "/:projectSlug/roles", + url: "/:projectId/roles", config: { rateLimit: writeLimit }, @@ -25,7 +26,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { } ], params: z.object({ - projectSlug: z.string().trim().describe(PROJECT_ROLE.CREATE.projectSlug) + projectId: z.string().trim().describe(PROJECT_ROLE.CREATE.projectId) }), body: z.object({ slug: z @@ -58,7 +59,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type, - projectSlug: req.params.projectSlug, + filter: { + type: ProjectRoleServiceIdentifierType.ID, + projectId: req.params.projectId + }, data: { ...req.body, permissions: JSON.stringify(packRules(req.body.permissions)) @@ -82,7 +86,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { } ], params: z.object({ - projectSlug: z.string().trim().describe(PROJECT_ROLE.UPDATE.projectSlug), + projectId: z.string().trim().describe(PROJECT_ROLE.UPDATE.projectId), roleId: z.string().trim().describe(PROJECT_ROLE.UPDATE.roleId) }), body: z.object({ @@ -118,7 +122,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type, - projectSlug: req.params.projectSlug, roleId: req.params.roleId, data: { ...req.body, @@ -131,7 +134,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { server.route({ method: "DELETE", - url: "/:projectSlug/roles/:roleId", + url: "/:projectId/roles/:roleId", config: { rateLimit: writeLimit }, @@ -143,7 +146,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { } ], params: z.object({ - projectSlug: z.string().trim().describe(PROJECT_ROLE.DELETE.projectSlug), + projectId: z.string().trim().describe(PROJECT_ROLE.DELETE.projectId), roleId: z.string().trim().describe(PROJECT_ROLE.DELETE.roleId) }), response: { @@ -159,7 +162,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type, - projectSlug: req.params.projectSlug, roleId: req.params.roleId }); return { role }; @@ -168,7 +170,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { server.route({ method: "GET", - url: "/:projectSlug/roles", + url: "/:projectId/roles", config: { rateLimit: readLimit }, @@ -180,7 +182,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { } ], params: z.object({ - projectSlug: z.string().trim().describe(PROJECT_ROLE.LIST.projectSlug) + projectId: z.string().trim().describe(PROJECT_ROLE.LIST.projectId) }), response: { 200: z.object({ @@ -195,7 +197,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type, - projectSlug: req.params.projectSlug + filter: { + type: ProjectRoleServiceIdentifierType.ID, + projectId: req.params.projectId + } }); return { roles }; } @@ -203,13 +208,13 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { server.route({ method: "GET", - url: "/:projectSlug/roles/slug/:roleSlug", + url: "/:projectId/roles/slug/:roleSlug", config: { rateLimit: readLimit }, schema: { params: z.object({ - projectSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.projectSlug), + projectId: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.projectId), roleSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.roleSlug) }), response: { @@ -225,48 +230,13 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type, - projectSlug: req.params.projectSlug, + filter: { + type: ProjectRoleServiceIdentifierType.ID, + projectId: req.params.projectId + }, roleSlug: req.params.roleSlug }); return { role }; } }); - - server.route({ - method: "GET", - url: "/:projectId/permissions", - config: { - rateLimit: readLimit - }, - schema: { - params: z.object({ - projectId: z.string().trim() - }), - response: { - 200: z.object({ - data: z.object({ - membership: ProjectMembershipsSchema.extend({ - roles: z - .object({ - role: z.string() - }) - .array() - }), - permissions: z.any().array() - }) - }) - } - }, - onRequest: verifyAuth([AuthMode.JWT]), - handler: async (req) => { - const { permissions, membership } = await server.services.projectRole.getUserPermission( - req.permission.id, - req.params.projectId, - req.permission.authMethod, - req.permission.orgId - ); - - return { data: { permissions, membership } }; - } - }); }; diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index af51d323d..97ff2ffb9 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -1367,6 +1367,7 @@ export const PKI_COLLECTIONS = { export const PROJECT_ROLE = { CREATE: { projectSlug: "Slug of the project to create the role for.", + projectId: "Id of the project to create the role for.", slug: "The slug of the role.", name: "The name of the role.", description: "The description for the role.", @@ -1374,6 +1375,7 @@ export const PROJECT_ROLE = { }, UPDATE: { projectSlug: "Slug of the project to update the role for.", + projectId: "Id of the project to update the role for.", roleId: "The ID of the role to update", slug: "The slug of the role.", name: "The name of the role.", @@ -1382,14 +1384,17 @@ export const PROJECT_ROLE = { }, DELETE: { projectSlug: "Slug of the project to delete this role for.", + projectId: "Id of the project to delete the role for.", roleId: "The ID of the role to update" }, GET_ROLE_BY_SLUG: { projectSlug: "The slug of the project.", + projectId: "The id of the project.", roleSlug: "The slug of the role to get details" }, LIST: { - projectSlug: "The slug of the project to list the roles of." + projectSlug: "The slug of the project to list the roles of.", + projectId: "The id of the project." } }; diff --git a/backend/src/services/project-role/project-role-service.ts b/backend/src/services/project-role/project-role-service.ts index e839752c5..95009f645 100644 --- a/backend/src/services/project-role/project-role-service.ts +++ b/backend/src/services/project-role/project-role-service.ts @@ -17,7 +17,14 @@ import { TProjectDALFactory } from "../project/project-dal"; import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; import { TProjectRoleDALFactory } from "./project-role-dal"; import { getPredefinedRoles } from "./project-role-fns"; -import { TCreateRoleDTO, TDeleteRoleDTO, TGetRoleBySlugDTO, TListRolesDTO, TUpdateRoleDTO } from "./project-role-types"; +import { + ProjectRoleServiceIdentifierType, + TCreateRoleDTO, + TDeleteRoleDTO, + TGetRoleDetailsDTO, + TListRolesDTO, + TUpdateRoleDTO +} from "./project-role-types"; type TProjectRoleServiceFactoryDep = { projectRoleDAL: TProjectRoleDALFactory; @@ -41,10 +48,15 @@ export const projectRoleServiceFactory = ({ projectUserMembershipRoleDAL, projectDAL }: TProjectRoleServiceFactoryDep) => { - const createRole = async ({ projectSlug, data, actor, actorId, actorAuthMethod, actorOrgId }: TCreateRoleDTO) => { - const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); - if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); - const projectId = project.id; + const createRole = async ({ data, actor, actorId, actorAuthMethod, actorOrgId, filter }: TCreateRoleDTO) => { + let projectId = ""; + if (filter.type === ProjectRoleServiceIdentifierType.SLUG) { + const project = await projectDAL.findProjectBySlug(filter.projectSlug, actorOrgId); + if (!project) throw new NotFoundError({ message: "Project not found" }); + projectId = project.id; + } else { + projectId = filter.projectId; + } const { permission } = await permissionService.getProjectPermission( actor, @@ -69,14 +81,19 @@ export const projectRoleServiceFactory = ({ const getRoleBySlug = async ({ actor, actorId, - projectSlug, actorAuthMethod, actorOrgId, - roleSlug - }: TGetRoleBySlugDTO) => { - const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); - if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); - const projectId = project.id; + roleSlug, + filter + }: TGetRoleDetailsDTO) => { + let projectId = ""; + if (filter.type === ProjectRoleServiceIdentifierType.SLUG) { + const project = await projectDAL.findProjectBySlug(filter.projectSlug, actorOrgId); + if (!project) throw new NotFoundError({ message: "Project not found" }); + projectId = project.id; + } else { + projectId = filter.projectId; + } const { permission } = await permissionService.getProjectPermission( actor, @@ -96,58 +113,41 @@ export const projectRoleServiceFactory = ({ return { ...customRole, permissions: unpackPermissions(customRole.permissions) }; }; - const updateRole = async ({ - roleId, - projectSlug, - actorOrgId, - actorAuthMethod, - actorId, - actor, - data - }: TUpdateRoleDTO) => { - const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); - if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); - const projectId = project.id; + const updateRole = async ({ roleId, actorOrgId, actorAuthMethod, actorId, actor, data }: TUpdateRoleDTO) => { + const projectRole = await projectRoleDAL.findById(roleId); + if (!projectRole) throw new NotFoundError({ message: "Project role not found", name: "Delete role" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - projectId, + projectRole.projectId, actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Role); if (data?.slug) { - const existingRole = await projectRoleDAL.findOne({ slug: data.slug, projectId }); + const existingRole = await projectRoleDAL.findOne({ slug: data.slug, projectId: projectRole.projectId }); if (existingRole && existingRole.id !== roleId) throw new BadRequestError({ name: "Update Role", message: "Project role with the same slug already exists" }); } - const [updatedRole] = await projectRoleDAL.update( - { id: roleId, projectId }, - { - ...data, - permissions: data.permissions ? data.permissions : undefined - } - ); - if (!updatedRole) { - throw new NotFoundError({ - message: `Project role with ID '${roleId}' in project with ID '${projectId}' not found` - }); - } + const updatedRole = await projectRoleDAL.updateById(projectRole.id, { + ...data, + permissions: data.permissions ? data.permissions : undefined + }); + if (!updatedRole) throw new NotFoundError({ message: "Project role not found", name: "Update role" }); + return { ...updatedRole, permissions: unpackPermissions(updatedRole.permissions) }; }; - const deleteRole = async ({ actor, actorId, actorAuthMethod, actorOrgId, projectSlug, roleId }: TDeleteRoleDTO) => { - const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); - if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); - const projectId = project.id; - + const deleteRole = async ({ actor, actorId, actorAuthMethod, actorOrgId, roleId }: TDeleteRoleDTO) => { + const projectRole = await projectRoleDAL.findById(roleId); + if (!projectRole) throw new NotFoundError({ message: "Project role not found", name: "Delete role" }); const { permission } = await permissionService.getProjectPermission( actor, actorId, - projectId, + projectRole.projectId, actorAuthMethod, actorOrgId ); @@ -169,21 +169,21 @@ export const projectRoleServiceFactory = ({ }); } - const [deletedRole] = await projectRoleDAL.delete({ id: roleId, projectId }); - if (!deletedRole) { - throw new NotFoundError({ - message: `Project role with ID '${roleId}' in project with ID '${projectId}' not found`, - name: "DeleteRole" - }); - } + const deletedRole = await projectRoleDAL.deleteById(roleId); + if (!deletedRole) throw new NotFoundError({ message: "Project role not found", name: "Delete role" }); return { ...deletedRole, permissions: unpackPermissions(deletedRole.permissions) }; }; - const listRoles = async ({ projectSlug, actorOrgId, actorAuthMethod, actorId, actor }: TListRolesDTO) => { - const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); - if (!project) throw new BadRequestError({ message: "Project not found" }); - const projectId = project.id; + const listRoles = async ({ actorOrgId, actorAuthMethod, actorId, actor, filter }: TListRolesDTO) => { + let projectId = ""; + if (filter.type === ProjectRoleServiceIdentifierType.SLUG) { + const project = await projectDAL.findProjectBySlug(filter.projectSlug, actorOrgId); + if (!project) throw new BadRequestError({ message: "Project not found" }); + projectId = project.id; + } else { + projectId = filter.projectId; + } const { permission } = await permissionService.getProjectPermission( actor, diff --git a/backend/src/services/project-role/project-role-types.ts b/backend/src/services/project-role/project-role-types.ts index 62b627a79..a71c73113 100644 --- a/backend/src/services/project-role/project-role-types.ts +++ b/backend/src/services/project-role/project-role-types.ts @@ -1,27 +1,36 @@ import { TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas"; import { TProjectPermission } from "@app/lib/types"; +export enum ProjectRoleServiceIdentifierType { + ID = "id", + SLUG = "slug" +} + export type TCreateRoleDTO = { data: Omit; - projectSlug: string; + filter: + | { type: ProjectRoleServiceIdentifierType.SLUG; projectSlug: string } + | { type: ProjectRoleServiceIdentifierType.ID; projectId: string }; } & Omit; -export type TGetRoleBySlugDTO = { +export type TGetRoleDetailsDTO = { roleSlug: string; - projectSlug: string; + filter: + | { type: ProjectRoleServiceIdentifierType.SLUG; projectSlug: string } + | { type: ProjectRoleServiceIdentifierType.ID; projectId: string }; } & Omit; export type TUpdateRoleDTO = { roleId: string; data: Omit; - projectSlug: string; } & Omit; export type TDeleteRoleDTO = { roleId: string; - projectSlug: string; } & Omit; export type TListRolesDTO = { - projectSlug: string; + filter: + | { type: ProjectRoleServiceIdentifierType.SLUG; projectSlug: string } + | { type: ProjectRoleServiceIdentifierType.ID; projectId: string }; } & Omit; diff --git a/frontend/src/hooks/api/roles/queries.tsx b/frontend/src/hooks/api/roles/queries.tsx index 1726abfd3..52bea6e58 100644 --- a/frontend/src/hooks/api/roles/queries.tsx +++ b/frontend/src/hooks/api/roles/queries.tsx @@ -136,7 +136,7 @@ const getUserProjectPermissions = async ({ workspaceId }: TGetUserProjectPermiss permissions: PackRule>>[]; membership: Omit & { roles: { role: string }[] }; }; - }>(`/api/v2/workspace/${workspaceId}/permissions`, {}); + }>(`/api/v1/workspace/${workspaceId}/permissions`, {}); return data.data; };