From a1bbd50c0bfdf8f4ce73f6da231f3152b7638693 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Tue, 29 Apr 2025 00:09:30 +0400 Subject: [PATCH 1/3] feat: build strict find filter --- backend/src/lib/knex/index.ts | 47 +++++++++++++++++++ .../secret-v2-bridge/secret-v2-bridge-dal.ts | 11 ++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/backend/src/lib/knex/index.ts b/backend/src/lib/knex/index.ts index d43d2af8e..c5dccc4bf 100644 --- a/backend/src/lib/knex/index.ts +++ b/backend/src/lib/knex/index.ts @@ -2,6 +2,8 @@ import { Knex } from "knex"; import { Tables } from "knex/types/tables"; +import { TableName } from "@app/db/schemas"; + import { DatabaseError } from "../errors"; import { buildDynamicKnexQuery, TKnexDynamicOperator } from "./dynamic"; @@ -25,6 +27,51 @@ export type TFindFilter = Partial & { $search?: Partial<{ [k in keyof R]: R[k] }>; $complex?: TKnexDynamicOperator; }; + +export const buildStrictFindFilter = + ( + { $in, $notNull, $search, $complex, ...filter }: TFindFilter, + tableName: TableName, + excludeKeys?: Array + ) => + (bd: Knex.QueryBuilder) => { + const strictFilter = Object.fromEntries( + Object.entries(filter) + .filter(([key]) => !excludeKeys || !excludeKeys.includes(key as keyof R)) + .map(([key, value]) => [`${tableName}.${key}`, value]) + ); + + void bd.where(strictFilter); + if ($in) { + Object.entries($in).forEach(([key, val]) => { + if (val) { + void bd.whereIn([`${tableName}.${key}`] as never, val as never); + } + }); + } + + if ($notNull?.length) { + $notNull.forEach((key) => { + void bd.whereNotNull([`${tableName}.${key as string}`] as never); + }); + } + + if ($search) { + Object.entries($search).forEach(([key, val]) => { + if (val) { + void bd.whereILike([`${tableName}.${key}`] as never, val as never); + } + }); + } + if ($complex) { + return buildDynamicKnexQuery(bd, $complex); + } + return bd; + }; + +/** + * @deprecated Use `buildStrictFindFilter` instead + */ export const buildFindFilter = ({ $in, $notNull, $search, $complex, ...filter }: TFindFilter) => (bd: Knex.QueryBuilder) => { diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts index cf389ac2a..813ab6385 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts @@ -10,6 +10,7 @@ import { generateCacheKeyFromData } from "@app/lib/crypto/cache"; import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors"; import { buildFindFilter, + buildStrictFindFilter, ormify, selectAllTableCols, sqlNestRelationships, @@ -63,7 +64,8 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => { const findOne = async (filter: Partial, tx?: Knex) => { try { const docs = await (tx || db)(TableName.SecretV2) - .where(filter) + // eslint-disable-next-line @typescript-eslint/no-misused-promises + .where(buildStrictFindFilter(filter, TableName.SecretV2)) .leftJoin( TableName.SecretV2JnTag, `${TableName.SecretV2}.id`, @@ -142,9 +144,14 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => { const find = async (filter: TFindFilter, opts: TFindOpt = {}) => { const { offset, limit, sort, tx } = opts; try { + const qualifiedFilter = { ...filter }; + if ("userId" in qualifiedFilter) { + delete qualifiedFilter.userId; + } + const query = (tx || db)(TableName.SecretV2) // eslint-disable-next-line @typescript-eslint/no-misused-promises - .where(buildFindFilter(filter)) + .where(buildFindFilter(qualifiedFilter)) .leftJoin( TableName.SecretV2JnTag, `${TableName.SecretV2}.id`, From 85965184f882128d67320ce39c933e3e0e631135 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Tue, 29 Apr 2025 00:18:13 +0400 Subject: [PATCH 2/3] Update secret-v2-bridge-dal.ts --- .../src/services/secret-v2-bridge/secret-v2-bridge-dal.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts index 813ab6385..69b1bc7c0 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts @@ -144,14 +144,9 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => { const find = async (filter: TFindFilter, opts: TFindOpt = {}) => { const { offset, limit, sort, tx } = opts; try { - const qualifiedFilter = { ...filter }; - if ("userId" in qualifiedFilter) { - delete qualifiedFilter.userId; - } - const query = (tx || db)(TableName.SecretV2) // eslint-disable-next-line @typescript-eslint/no-misused-promises - .where(buildFindFilter(qualifiedFilter)) + .where(buildFindFilter(filter)) .leftJoin( TableName.SecretV2JnTag, `${TableName.SecretV2}.id`, From e870e350026b28b1d0a5f1aa393e525fd0e62076 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Tue, 29 Apr 2025 04:27:10 +0400 Subject: [PATCH 3/3] consolidated filtering functions into one --- backend/src/lib/knex/index.ts | 58 +++++-------------- .../secret-v2-bridge/secret-v2-bridge-dal.ts | 3 +- 2 files changed, 14 insertions(+), 47 deletions(-) diff --git a/backend/src/lib/knex/index.ts b/backend/src/lib/knex/index.ts index c5dccc4bf..55d4bf399 100644 --- a/backend/src/lib/knex/index.ts +++ b/backend/src/lib/knex/index.ts @@ -28,72 +28,40 @@ export type TFindFilter = Partial & { $complex?: TKnexDynamicOperator; }; -export const buildStrictFindFilter = +export const buildFindFilter = ( { $in, $notNull, $search, $complex, ...filter }: TFindFilter, - tableName: TableName, + tableName?: TableName, excludeKeys?: Array ) => (bd: Knex.QueryBuilder) => { - const strictFilter = Object.fromEntries( - Object.entries(filter) - .filter(([key]) => !excludeKeys || !excludeKeys.includes(key as keyof R)) - .map(([key, value]) => [`${tableName}.${key}`, value]) - ); + const processedFilter = tableName + ? Object.fromEntries( + Object.entries(filter) + .filter(([key]) => !excludeKeys || !excludeKeys.includes(key as keyof R)) + .map(([key, value]) => [`${tableName}.${key}`, value]) + ) + : filter; - void bd.where(strictFilter); + void bd.where(processedFilter); if ($in) { Object.entries($in).forEach(([key, val]) => { if (val) { - void bd.whereIn([`${tableName}.${key}`] as never, val as never); + void bd.whereIn([`${tableName ? `${tableName}.` : ""}${key}`] as never, val as never); } }); } if ($notNull?.length) { $notNull.forEach((key) => { - void bd.whereNotNull([`${tableName}.${key as string}`] as never); + void bd.whereNotNull([`${tableName ? `${tableName}.` : ""}${key as string}`] as never); }); } if ($search) { Object.entries($search).forEach(([key, val]) => { if (val) { - void bd.whereILike([`${tableName}.${key}`] as never, val as never); - } - }); - } - if ($complex) { - return buildDynamicKnexQuery(bd, $complex); - } - return bd; - }; - -/** - * @deprecated Use `buildStrictFindFilter` instead - */ -export const buildFindFilter = - ({ $in, $notNull, $search, $complex, ...filter }: TFindFilter) => - (bd: Knex.QueryBuilder) => { - void bd.where(filter); - if ($in) { - Object.entries($in).forEach(([key, val]) => { - if (val) { - void bd.whereIn(key as never, val as never); - } - }); - } - - if ($notNull?.length) { - $notNull.forEach((key) => { - void bd.whereNotNull(key as never); - }); - } - - if ($search) { - Object.entries($search).forEach(([key, val]) => { - if (val) { - void bd.whereILike(key as never, val as never); + void bd.whereILike([`${tableName ? `${tableName}.` : ""}${key}`] as never, val as never); } }); } diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts index 69b1bc7c0..27a3c94e5 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-dal.ts @@ -10,7 +10,6 @@ import { generateCacheKeyFromData } from "@app/lib/crypto/cache"; import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors"; import { buildFindFilter, - buildStrictFindFilter, ormify, selectAllTableCols, sqlNestRelationships, @@ -65,7 +64,7 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => { try { const docs = await (tx || db)(TableName.SecretV2) // eslint-disable-next-line @typescript-eslint/no-misused-promises - .where(buildStrictFindFilter(filter, TableName.SecretV2)) + .where(buildFindFilter(filter, TableName.SecretV2)) .leftJoin( TableName.SecretV2JnTag, `${TableName.SecretV2}.id`,