From bbceb37d065e7c3ce8d37ecda415b8ff9c60c93b Mon Sep 17 00:00:00 2001 From: Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> Date: Sat, 24 Feb 2024 05:03:54 +0100 Subject: [PATCH] feat: fix project query by slug (now accepts an org ID) --- .../src/server/routes/v1/project-router.ts | 18 ++++++++----- .../src/server/routes/v2/project-router.ts | 21 ++++++++++----- backend/src/services/project/project-dal.ts | 27 +++++++++++++------ .../src/services/project/project-service.ts | 12 ++++----- backend/src/services/project/project-types.ts | 20 +++++++++----- 5 files changed, 66 insertions(+), 32 deletions(-) diff --git a/backend/src/server/routes/v1/project-router.ts b/backend/src/server/routes/v1/project-router.ts index bd27781412..c76504ee07 100644 --- a/backend/src/server/routes/v1/project-router.ts +++ b/backend/src/server/routes/v1/project-router.ts @@ -138,8 +138,10 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { onRequest: verifyAuth([AuthMode.JWT, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const workspace = await server.services.project.getAProject({ - filterType: ProjectFilterType.ID, - filter: req.params.workspaceId, + filter: { + type: ProjectFilterType.ID, + projectId: req.params.workspaceId + }, actorId: req.permission.id, actor: req.permission.type, actorOrgId: req.permission.orgId @@ -191,8 +193,10 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const workspace = await server.services.project.deleteProject({ - filterType: ProjectFilterType.ID, - filter: req.params.workspaceId, + filter: { + type: ProjectFilterType.ID, + projectId: req.params.workspaceId + }, actorId: req.permission.id, actor: req.permission.type, actorOrgId: req.permission.orgId @@ -259,8 +263,10 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const workspace = await server.services.project.updateProject({ - filterType: ProjectFilterType.ID, - filter: req.params.workspaceId, + filter: { + type: ProjectFilterType.ID, + projectId: req.params.workspaceId + }, update: { name: req.body.name, autoCapitalization: req.body.autoCapitalization diff --git a/backend/src/server/routes/v2/project-router.ts b/backend/src/server/routes/v2/project-router.ts index 1b6e0a3e48..91d7b26842 100644 --- a/backend/src/server/routes/v2/project-router.ts +++ b/backend/src/server/routes/v2/project-router.ts @@ -195,8 +195,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { handler: async (req) => { const project = await server.services.project.deleteProject({ - filterType: ProjectFilterType.SLUG, - filter: req.params.slug, + filter: { + type: ProjectFilterType.SLUG, + slug: req.params.slug, + orgId: req.permission.orgId + }, actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type @@ -221,8 +224,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const project = await server.services.project.getAProject({ - filter: req.params.slug, - filterType: ProjectFilterType.SLUG, + filter: { + slug: req.params.slug, + orgId: req.permission.orgId, + type: ProjectFilterType.SLUG + }, actorId: req.permission.id, actorOrgId: req.permission.orgId, actor: req.permission.type @@ -252,8 +258,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const project = await server.services.project.updateProject({ - filterType: ProjectFilterType.SLUG, - filter: req.params.slug, + filter: { + type: ProjectFilterType.SLUG, + slug: req.params.slug, + orgId: req.permission.orgId + }, update: { name: req.body.name, autoCapitalization: req.body.autoCapitalization diff --git a/backend/src/services/project/project-dal.ts b/backend/src/services/project/project-dal.ts index d629b1d0f4..369e005acc 100644 --- a/backend/src/services/project/project-dal.ts +++ b/backend/src/services/project/project-dal.ts @@ -5,7 +5,7 @@ import { ProjectsSchema, ProjectUpgradeStatus, ProjectVersion, TableName, TProje import { BadRequestError, DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex"; -import { ProjectFilterType } from "./project-types"; +import { Filter, ProjectFilterType } from "./project-types"; export type TProjectDALFactory = ReturnType; @@ -168,10 +168,11 @@ export const projectDALFactory = (db: TDbClient) => { } }; - const findProjectBySlug = async (slug: string) => { + const findProjectBySlug = async (slug: string, orgId: string) => { try { const projects = await db(TableName.ProjectMembership) .where(`${TableName.Project}.slug`, slug) + .where(`${TableName.Project}.orgId`, orgId) .join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) .join(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`) .select( @@ -185,6 +186,7 @@ export const projectDALFactory = (db: TDbClient) => { { column: `${TableName.Project}.name`, order: "asc" }, { column: `${TableName.Environment}.position`, order: "asc" } ]); + const project = sqlNestRelationships({ data: projects, key: "id", @@ -212,17 +214,26 @@ export const projectDALFactory = (db: TDbClient) => { } }; - const findProjectByFilter = async (filter: string, type: ProjectFilterType) => { + const findProjectByFilter = async (filter: Filter) => { try { - if (type === ProjectFilterType.ID) { - return await findProjectById(filter); + if (filter.type === ProjectFilterType.ID) { + return await findProjectById(filter.projectId); } - if (type === ProjectFilterType.SLUG) { - return await findProjectBySlug(filter); + if (filter.type === ProjectFilterType.SLUG) { + if (!filter.orgId) { + throw new BadRequestError({ + message: "Organization ID is required when querying with slugs" + }); + } + + return await findProjectBySlug(filter.slug, filter.orgId); } throw new BadRequestError({ message: "Invalid filter type" }); } catch (error) { - throw new DatabaseError({ error, name: `Failed to find project by ${type}` }); + if (error instanceof BadRequestError) { + throw error; + } + throw new DatabaseError({ error, name: `Failed to find project by ${filter.type}` }); } }; diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index 4c647f391f..6048c142af 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -312,8 +312,8 @@ export const projectServiceFactory = ({ return results; }; - const deleteProject = async ({ actor, actorId, actorOrgId, filter, filterType }: TDeleteProjectDTO) => { - const project = await projectDAL.findProjectByFilter(filter, filterType); + const deleteProject = async ({ actor, actorId, actorOrgId, filter }: TDeleteProjectDTO) => { + const project = await projectDAL.findProjectByFilter(filter); const { permission } = await permissionService.getProjectPermission(actor, actorId, project.id, actorOrgId); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Project); @@ -338,15 +338,15 @@ export const projectServiceFactory = ({ return workspaces; }; - const getAProject = async ({ actorId, actorOrgId, filter, filterType, actor }: TGetProjectDTO) => { - const project = await projectDAL.findProjectByFilter(filter, filterType); + const getAProject = async ({ actorId, actorOrgId, filter, actor }: TGetProjectDTO) => { + const project = await projectDAL.findProjectByFilter(filter); await permissionService.getProjectPermission(actor, actorId, project.id, actorOrgId); return project; }; - const updateProject = async ({ actor, actorId, actorOrgId, update, filter, filterType }: TUpdateProjectDTO) => { - const project = await projectDAL.findProjectByFilter(filter, filterType); + const updateProject = async ({ actor, actorId, actorOrgId, update, filter }: TUpdateProjectDTO) => { + const project = await projectDAL.findProjectByFilter(filter); const { permission } = await permissionService.getProjectPermission(actor, actorId, project.id, actorOrgId); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); diff --git a/backend/src/services/project/project-types.ts b/backend/src/services/project/project-types.ts index f43930c47a..ace09d2b0c 100644 --- a/backend/src/services/project/project-types.ts +++ b/backend/src/services/project/project-types.ts @@ -8,6 +8,17 @@ export enum ProjectFilterType { SLUG = "slug" } +export type Filter = + | { + type: ProjectFilterType.ID; + projectId: string; + } + | { + type: ProjectFilterType.SLUG; + slug: string; + orgId: string | undefined; + }; + export type TCreateProjectDTO = { actor: ActorType; actorId: string; @@ -25,8 +36,7 @@ export type TDeleteProjectBySlugDTO = { }; export type TGetProjectDTO = { - filter: string; - filterType: ProjectFilterType; + filter: Filter; } & Omit; export type TToggleProjectAutoCapitalizationDTO = { @@ -37,8 +47,7 @@ export type TUpdateProjectNameDTO = { } & TProjectPermission; export type TUpdateProjectDTO = { - filter: string; - filterType: ProjectFilterType; + filter: Filter; update: { name?: string; autoCapitalization?: boolean; @@ -46,8 +55,7 @@ export type TUpdateProjectDTO = { } & Omit; export type TDeleteProjectDTO = { - filter: string; - filterType: ProjectFilterType; + filter: Filter; actor: ActorType; actorId: string; actorOrgId?: string;