Merge pull request #14741 from Budibase/budi-8708-single-and-multi-select-user-fields-appear-to-no-longer-be

Allow conditional search for users
This commit is contained in:
Adria Navarro 2024-10-09 11:04:29 +02:00 committed by GitHub
commit 47985748d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 47 additions and 10 deletions

View File

@ -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 }
@ -262,10 +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<User>(
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: any) => row.doc)
const response = await db.allDocs<User>(getGlobalUserParams(null, opts))
userList = response.rows.map(row => row.doc!)
}
return pagination(userList, pageSize, {
paginate: true,

View File

@ -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)

View File

@ -639,19 +639,19 @@ export function fixupFilterArrays(filters: SearchFilters) {
return filters
}
export function search<T>(
docs: Record<string, T>[],
query: RowSearchParams
): SearchResponse<Record<string, T>> {
export function search<T extends Record<string, any>>(
docs: T[],
query: Omit<RowSearchParams, "tableId">
): SearchResponse<T> {
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<Record<string, any>> = { rows: result }
const response: SearchResponse<T> = { rows: result }
if (query.countRows) {
response.totalRows = totalRows
}

View File

@ -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) {

View File

@ -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", () => {