feat: made v2 project role router use projectid instead of projectslug

This commit is contained in:
=
2024-10-25 00:59:39 +05:30
parent a749e70815
commit 558a809b4c
6 changed files with 112 additions and 120 deletions
@@ -12,6 +12,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedRoleSchemaV1 } from "@app/server/routes/sanitizedSchemas"; import { SanitizedRoleSchemaV1 } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectRoleServiceIdentifierType } from "@app/services/project-role/project-role-types";
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
@@ -61,7 +62,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug, filter: {
type: ProjectRoleServiceIdentifierType.SLUG,
projectSlug: req.params.projectSlug
},
data: { data: {
...req.body, ...req.body,
permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions))) permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions)))
@@ -121,7 +125,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug,
roleId: req.params.roleId, roleId: req.params.roleId,
data: { data: {
...req.body, ...req.body,
@@ -164,7 +167,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug,
roleId: req.params.roleId roleId: req.params.roleId
}); });
return { role }; return { role };
@@ -200,7 +202,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug filter: {
type: ProjectRoleServiceIdentifierType.SLUG,
projectSlug: req.params.projectSlug
}
}); });
return { roles }; return { roles };
} }
@@ -230,7 +235,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug, filter: {
type: ProjectRoleServiceIdentifierType.SLUG,
projectSlug: req.params.projectSlug
},
roleSlug: req.params.slug roleSlug: req.params.slug
}); });
return { role }; return { role };
+23 -53
View File
@@ -2,18 +2,19 @@ import { packRules } from "@casl/ability/extra";
import slugify from "@sindresorhus/slugify"; import slugify from "@sindresorhus/slugify";
import { z } from "zod"; 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 { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
import { PROJECT_ROLE } from "@app/lib/api-docs"; import { PROJECT_ROLE } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas"; import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectRoleServiceIdentifierType } from "@app/services/project-role/project-role-types";
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "POST", method: "POST",
url: "/:projectSlug/roles", url: "/:projectId/roles",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -25,7 +26,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
} }
], ],
params: z.object({ params: z.object({
projectSlug: z.string().trim().describe(PROJECT_ROLE.CREATE.projectSlug) projectId: z.string().trim().describe(PROJECT_ROLE.CREATE.projectId)
}), }),
body: z.object({ body: z.object({
slug: z slug: z
@@ -58,7 +59,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug, filter: {
type: ProjectRoleServiceIdentifierType.ID,
projectId: req.params.projectId
},
data: { data: {
...req.body, ...req.body,
permissions: JSON.stringify(packRules(req.body.permissions)) permissions: JSON.stringify(packRules(req.body.permissions))
@@ -82,7 +86,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
} }
], ],
params: z.object({ 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) roleId: z.string().trim().describe(PROJECT_ROLE.UPDATE.roleId)
}), }),
body: z.object({ body: z.object({
@@ -118,7 +122,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug,
roleId: req.params.roleId, roleId: req.params.roleId,
data: { data: {
...req.body, ...req.body,
@@ -131,7 +134,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "DELETE", method: "DELETE",
url: "/:projectSlug/roles/:roleId", url: "/:projectId/roles/:roleId",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -143,7 +146,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
} }
], ],
params: z.object({ 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) roleId: z.string().trim().describe(PROJECT_ROLE.DELETE.roleId)
}), }),
response: { response: {
@@ -159,7 +162,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug,
roleId: req.params.roleId roleId: req.params.roleId
}); });
return { role }; return { role };
@@ -168,7 +170,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "GET", method: "GET",
url: "/:projectSlug/roles", url: "/:projectId/roles",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
@@ -180,7 +182,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
} }
], ],
params: z.object({ params: z.object({
projectSlug: z.string().trim().describe(PROJECT_ROLE.LIST.projectSlug) projectId: z.string().trim().describe(PROJECT_ROLE.LIST.projectId)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -195,7 +197,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug filter: {
type: ProjectRoleServiceIdentifierType.ID,
projectId: req.params.projectId
}
}); });
return { roles }; return { roles };
} }
@@ -203,13 +208,13 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "GET", method: "GET",
url: "/:projectSlug/roles/slug/:roleSlug", url: "/:projectId/roles/slug/:roleSlug",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
schema: { schema: {
params: z.object({ 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) roleSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.roleSlug)
}), }),
response: { response: {
@@ -225,48 +230,13 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
actor: req.permission.type, actor: req.permission.type,
projectSlug: req.params.projectSlug, filter: {
type: ProjectRoleServiceIdentifierType.ID,
projectId: req.params.projectId
},
roleSlug: req.params.roleSlug roleSlug: req.params.roleSlug
}); });
return { role }; 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 } };
}
});
}; };
+6 -1
View File
@@ -1367,6 +1367,7 @@ export const PKI_COLLECTIONS = {
export const PROJECT_ROLE = { export const PROJECT_ROLE = {
CREATE: { CREATE: {
projectSlug: "Slug of the project to create the role for.", 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.", slug: "The slug of the role.",
name: "The name of the role.", name: "The name of the role.",
description: "The description for the role.", description: "The description for the role.",
@@ -1374,6 +1375,7 @@ export const PROJECT_ROLE = {
}, },
UPDATE: { UPDATE: {
projectSlug: "Slug of the project to update the role for.", 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", roleId: "The ID of the role to update",
slug: "The slug of the role.", slug: "The slug of the role.",
name: "The name of the role.", name: "The name of the role.",
@@ -1382,14 +1384,17 @@ export const PROJECT_ROLE = {
}, },
DELETE: { DELETE: {
projectSlug: "Slug of the project to delete this role for.", 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" roleId: "The ID of the role to update"
}, },
GET_ROLE_BY_SLUG: { GET_ROLE_BY_SLUG: {
projectSlug: "The slug of the project.", projectSlug: "The slug of the project.",
projectId: "The id of the project.",
roleSlug: "The slug of the role to get details" roleSlug: "The slug of the role to get details"
}, },
LIST: { 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."
} }
}; };
@@ -17,7 +17,14 @@ import { TProjectDALFactory } from "../project/project-dal";
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal"; import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
import { TProjectRoleDALFactory } from "./project-role-dal"; import { TProjectRoleDALFactory } from "./project-role-dal";
import { getPredefinedRoles } from "./project-role-fns"; 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 = { type TProjectRoleServiceFactoryDep = {
projectRoleDAL: TProjectRoleDALFactory; projectRoleDAL: TProjectRoleDALFactory;
@@ -41,10 +48,15 @@ export const projectRoleServiceFactory = ({
projectUserMembershipRoleDAL, projectUserMembershipRoleDAL,
projectDAL projectDAL
}: TProjectRoleServiceFactoryDep) => { }: TProjectRoleServiceFactoryDep) => {
const createRole = async ({ projectSlug, data, actor, actorId, actorAuthMethod, actorOrgId }: TCreateRoleDTO) => { const createRole = async ({ data, actor, actorId, actorAuthMethod, actorOrgId, filter }: TCreateRoleDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); let projectId = "";
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); if (filter.type === ProjectRoleServiceIdentifierType.SLUG) {
const projectId = project.id; 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( const { permission } = await permissionService.getProjectPermission(
actor, actor,
@@ -69,14 +81,19 @@ export const projectRoleServiceFactory = ({
const getRoleBySlug = async ({ const getRoleBySlug = async ({
actor, actor,
actorId, actorId,
projectSlug,
actorAuthMethod, actorAuthMethod,
actorOrgId, actorOrgId,
roleSlug roleSlug,
}: TGetRoleBySlugDTO) => { filter
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); }: TGetRoleDetailsDTO) => {
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); let projectId = "";
const projectId = project.id; 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( const { permission } = await permissionService.getProjectPermission(
actor, actor,
@@ -96,58 +113,41 @@ export const projectRoleServiceFactory = ({
return { ...customRole, permissions: unpackPermissions(customRole.permissions) }; return { ...customRole, permissions: unpackPermissions(customRole.permissions) };
}; };
const updateRole = async ({ const updateRole = async ({ roleId, actorOrgId, actorAuthMethod, actorId, actor, data }: TUpdateRoleDTO) => {
roleId, const projectRole = await projectRoleDAL.findById(roleId);
projectSlug, if (!projectRole) throw new NotFoundError({ message: "Project role not found", name: "Delete role" });
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 { permission } = await permissionService.getProjectPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
projectId, projectRole.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Role); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Role);
if (data?.slug) { 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) if (existingRole && existingRole.id !== roleId)
throw new BadRequestError({ name: "Update Role", message: "Project role with the same slug already exists" }); throw new BadRequestError({ name: "Update Role", message: "Project role with the same slug already exists" });
} }
const [updatedRole] = await projectRoleDAL.update( const updatedRole = await projectRoleDAL.updateById(projectRole.id, {
{ id: roleId, projectId }, ...data,
{ permissions: data.permissions ? data.permissions : undefined
...data, });
permissions: data.permissions ? data.permissions : undefined if (!updatedRole) throw new NotFoundError({ message: "Project role not found", name: "Update role" });
}
);
if (!updatedRole) {
throw new NotFoundError({
message: `Project role with ID '${roleId}' in project with ID '${projectId}' not found`
});
}
return { ...updatedRole, permissions: unpackPermissions(updatedRole.permissions) }; return { ...updatedRole, permissions: unpackPermissions(updatedRole.permissions) };
}; };
const deleteRole = async ({ actor, actorId, actorAuthMethod, actorOrgId, projectSlug, roleId }: TDeleteRoleDTO) => { const deleteRole = async ({ actor, actorId, actorAuthMethod, actorOrgId, roleId }: TDeleteRoleDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); const projectRole = await projectRoleDAL.findById(roleId);
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); if (!projectRole) throw new NotFoundError({ message: "Project role not found", name: "Delete role" });
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
projectId, projectRole.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
@@ -169,21 +169,21 @@ export const projectRoleServiceFactory = ({
}); });
} }
const [deletedRole] = await projectRoleDAL.delete({ id: roleId, projectId }); const deletedRole = await projectRoleDAL.deleteById(roleId);
if (!deletedRole) { if (!deletedRole) throw new NotFoundError({ message: "Project role not found", name: "Delete role" });
throw new NotFoundError({
message: `Project role with ID '${roleId}' in project with ID '${projectId}' not found`,
name: "DeleteRole"
});
}
return { ...deletedRole, permissions: unpackPermissions(deletedRole.permissions) }; return { ...deletedRole, permissions: unpackPermissions(deletedRole.permissions) };
}; };
const listRoles = async ({ projectSlug, actorOrgId, actorAuthMethod, actorId, actor }: TListRolesDTO) => { const listRoles = async ({ actorOrgId, actorAuthMethod, actorId, actor, filter }: TListRolesDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); let projectId = "";
if (!project) throw new BadRequestError({ message: "Project not found" }); if (filter.type === ProjectRoleServiceIdentifierType.SLUG) {
const projectId = project.id; 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( const { permission } = await permissionService.getProjectPermission(
actor, actor,
@@ -1,27 +1,36 @@
import { TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas"; import { TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas";
import { TProjectPermission } from "@app/lib/types"; import { TProjectPermission } from "@app/lib/types";
export enum ProjectRoleServiceIdentifierType {
ID = "id",
SLUG = "slug"
}
export type TCreateRoleDTO = { export type TCreateRoleDTO = {
data: Omit<TProjectRolesInsert, "projectId">; data: Omit<TProjectRolesInsert, "projectId">;
projectSlug: string; filter:
| { type: ProjectRoleServiceIdentifierType.SLUG; projectSlug: string }
| { type: ProjectRoleServiceIdentifierType.ID; projectId: string };
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TGetRoleBySlugDTO = { export type TGetRoleDetailsDTO = {
roleSlug: string; roleSlug: string;
projectSlug: string; filter:
| { type: ProjectRoleServiceIdentifierType.SLUG; projectSlug: string }
| { type: ProjectRoleServiceIdentifierType.ID; projectId: string };
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TUpdateRoleDTO = { export type TUpdateRoleDTO = {
roleId: string; roleId: string;
data: Omit<TOrgRolesUpdate, "orgId">; data: Omit<TOrgRolesUpdate, "orgId">;
projectSlug: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TDeleteRoleDTO = { export type TDeleteRoleDTO = {
roleId: string; roleId: string;
projectSlug: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TListRolesDTO = { export type TListRolesDTO = {
projectSlug: string; filter:
| { type: ProjectRoleServiceIdentifierType.SLUG; projectSlug: string }
| { type: ProjectRoleServiceIdentifierType.ID; projectId: string };
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
+1 -1
View File
@@ -136,7 +136,7 @@ const getUserProjectPermissions = async ({ workspaceId }: TGetUserProjectPermiss
permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[]; permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[];
membership: Omit<TProjectMembership, "roles"> & { roles: { role: string }[] }; membership: Omit<TProjectMembership, "roles"> & { roles: { role: string }[] };
}; };
}>(`/api/v2/workspace/${workspaceId}/permissions`, {}); }>(`/api/v1/workspace/${workspaceId}/permissions`, {});
return data.data; return data.data;
}; };