From bf62153b8b28b4688e4ee777c312561d881a9cfb Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 10:16:32 +0200 Subject: [PATCH 1/6] Add test with conditions --- .../src/api/routes/global/tests/users.spec.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/worker/src/api/routes/global/tests/users.spec.ts b/packages/worker/src/api/routes/global/tests/users.spec.ts index a654c42359..c8e71f7eb4 100644 --- a/packages/worker/src/api/routes/global/tests/users.spec.ts +++ b/packages/worker/src/api/routes/global/tests/users.spec.ts @@ -741,6 +741,25 @@ describe("/api/global/users", () => { it("should throw an error if public query performed", async () => { await config.api.users.searchUsers({}, { status: 403, noHeaders: true }) }) + + it("should be able to search using logical conditions", async () => { + const user = await config.createUser() + const response = await config.api.users.searchUsers({ + query: { + $and: { + conditions: [ + { + $and: { + conditions: [{ string: { email: user.email } }], + }, + }, + ], + }, + }, + }) + expect(response.body.data.length).toBe(1) + expect(response.body.data[0].email).toBe(user.email) + }) }) describe("DELETE /api/global/users/:userId", () => { From 0e24df2ddf125c1c9d11d20273712007163a2797 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 10:32:12 +0200 Subject: [PATCH 2/6] Improve types --- packages/shared-core/src/filters.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index b10375acb0..40c70a8a23 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -639,19 +639,19 @@ export function fixupFilterArrays(filters: SearchFilters) { return filters } -export function search( - docs: Record[], - query: RowSearchParams -): SearchResponse> { +export function search>( + docs: T[], + query: Omit +): SearchResponse { let result = runQuery(docs, query.query) if (query.sort) { result = sort(result, query.sort, query.sortOrder || SortOrder.ASCENDING) } - let totalRows = result.length + const totalRows = result.length if (query.limit) { result = limit(result, query.limit.toString()) } - const response: SearchResponse> = { rows: result } + const response: SearchResponse = { rows: result } if (query.countRows) { response.totalRows = totalRows } From 8b0c84b2ea67aa65a19f85b539d3e518bacd1aaf Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 10:32:39 +0200 Subject: [PATCH 3/6] In memory filters --- packages/backend-core/src/users/users.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/backend-core/src/users/users.ts b/packages/backend-core/src/users/users.ts index d8546afa8b..96fb79c0d0 100644 --- a/packages/backend-core/src/users/users.ts +++ b/packages/backend-core/src/users/users.ts @@ -24,6 +24,7 @@ import * as context from "../context" import { getGlobalDB } from "../context" import { isCreator } from "./utils" import { UserDB } from "./db" +import { dataFilters } from "@budibase/shared-core" type GetOpts = { cleanup?: boolean } @@ -263,9 +264,12 @@ export async function paginatedUsers({ cleanup: true, }) } else { - // no search, query allDocs - const response = await db.allDocs(getGlobalUserParams(null, opts)) - userList = response.rows.map((row: any) => row.doc) + const response = await db.allDocs(getGlobalUserParams(null, opts)) + userList = response.rows.map(row => row.doc!) + + if (query) { + userList = dataFilters.search(userList, { query }).rows + } } return pagination(userList, pageSize, { paginate: true, From 3cf96c589cd102958529d88c2b806f238aa3cd4d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 10:33:15 +0200 Subject: [PATCH 4/6] Implement logical conditions --- packages/shared-core/src/utils.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/shared-core/src/utils.ts b/packages/shared-core/src/utils.ts index 14b3c84425..4847e911e9 100644 --- a/packages/shared-core/src/utils.ts +++ b/packages/shared-core/src/utils.ts @@ -5,6 +5,7 @@ import { SearchFilters, BasicOperator, ArrayOperator, + isLogicalSearchOperator, } from "@budibase/types" import * as Constants from "./constants" import { removeKeyNumbering } from "./filters" @@ -97,10 +98,20 @@ export function isSupportedUserSearch(query: SearchFilters) { { op: BasicOperator.EQUAL, key: "_id" }, { op: ArrayOperator.ONE_OF, key: "_id" }, ] - for (let [key, operation] of Object.entries(query)) { + for (const [key, operation] of Object.entries(query)) { if (typeof operation !== "object") { return false } + + if (isLogicalSearchOperator(key)) { + for (const condition of query[key]!.conditions) { + if (!isSupportedUserSearch(condition)) { + return false + } + } + return true + } + const fields = Object.keys(operation || {}) // this filter doesn't contain options - ignore if (fields.length === 0) { From d811b9527f82ce55f1bae7c5c0a4a3bbfdeee84b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 10:44:04 +0200 Subject: [PATCH 5/6] Fix limit issues --- packages/backend-core/src/users/users.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/backend-core/src/users/users.ts b/packages/backend-core/src/users/users.ts index 96fb79c0d0..f4838597b6 100644 --- a/packages/backend-core/src/users/users.ts +++ b/packages/backend-core/src/users/users.ts @@ -263,13 +263,17 @@ export async function paginatedUsers({ userList = await bulkGetGlobalUsersById(query?.oneOf?._id, { cleanup: true, }) + } else if (query) { + // TODO: this should use SQS search, but the logic is built in the 'server' package. Using the in-memory filtering to get this working meanwhile + const response = await db.allDocs( + getGlobalUserParams(null, { ...opts, limit: undefined }) + ) + userList = response.rows.map(row => row.doc!) + userList = dataFilters.search(userList, { query, limit: opts.limit }).rows } else { + // no search, query allDocs const response = await db.allDocs(getGlobalUserParams(null, opts)) userList = response.rows.map(row => row.doc!) - - if (query) { - userList = dataFilters.search(userList, { query }).rows - } } return pagination(userList, pageSize, { paginate: true, From c2a5f673aeb4df00ce3f31cf14cb7594fc1bc926 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 10:51:51 +0200 Subject: [PATCH 6/6] Lint --- packages/server/src/api/routes/tests/search.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 51965b5574..1ccc9bfdc9 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -187,7 +187,6 @@ describe.each([ if (isInMemory) { return dataFilters.search(_.cloneDeep(rows), { ...this.query, - tableId: tableOrViewId, }) } else { return config.api.row.search(tableOrViewId, this.query)