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:
commit
47985748d2
|
@ -24,6 +24,7 @@ import * as context from "../context"
|
||||||
import { getGlobalDB } from "../context"
|
import { getGlobalDB } from "../context"
|
||||||
import { isCreator } from "./utils"
|
import { isCreator } from "./utils"
|
||||||
import { UserDB } from "./db"
|
import { UserDB } from "./db"
|
||||||
|
import { dataFilters } from "@budibase/shared-core"
|
||||||
|
|
||||||
type GetOpts = { cleanup?: boolean }
|
type GetOpts = { cleanup?: boolean }
|
||||||
|
|
||||||
|
@ -262,10 +263,17 @@ export async function paginatedUsers({
|
||||||
userList = await bulkGetGlobalUsersById(query?.oneOf?._id, {
|
userList = await bulkGetGlobalUsersById(query?.oneOf?._id, {
|
||||||
cleanup: true,
|
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 {
|
} else {
|
||||||
// no search, query allDocs
|
// no search, query allDocs
|
||||||
const response = await db.allDocs(getGlobalUserParams(null, opts))
|
const response = await db.allDocs<User>(getGlobalUserParams(null, opts))
|
||||||
userList = response.rows.map((row: any) => row.doc)
|
userList = response.rows.map(row => row.doc!)
|
||||||
}
|
}
|
||||||
return pagination(userList, pageSize, {
|
return pagination(userList, pageSize, {
|
||||||
paginate: true,
|
paginate: true,
|
||||||
|
|
|
@ -187,7 +187,6 @@ describe.each([
|
||||||
if (isInMemory) {
|
if (isInMemory) {
|
||||||
return dataFilters.search(_.cloneDeep(rows), {
|
return dataFilters.search(_.cloneDeep(rows), {
|
||||||
...this.query,
|
...this.query,
|
||||||
tableId: tableOrViewId,
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return config.api.row.search(tableOrViewId, this.query)
|
return config.api.row.search(tableOrViewId, this.query)
|
||||||
|
|
|
@ -639,19 +639,19 @@ export function fixupFilterArrays(filters: SearchFilters) {
|
||||||
return filters
|
return filters
|
||||||
}
|
}
|
||||||
|
|
||||||
export function search<T>(
|
export function search<T extends Record<string, any>>(
|
||||||
docs: Record<string, T>[],
|
docs: T[],
|
||||||
query: RowSearchParams
|
query: Omit<RowSearchParams, "tableId">
|
||||||
): SearchResponse<Record<string, T>> {
|
): SearchResponse<T> {
|
||||||
let result = runQuery(docs, query.query)
|
let result = runQuery(docs, query.query)
|
||||||
if (query.sort) {
|
if (query.sort) {
|
||||||
result = sort(result, query.sort, query.sortOrder || SortOrder.ASCENDING)
|
result = sort(result, query.sort, query.sortOrder || SortOrder.ASCENDING)
|
||||||
}
|
}
|
||||||
let totalRows = result.length
|
const totalRows = result.length
|
||||||
if (query.limit) {
|
if (query.limit) {
|
||||||
result = limit(result, query.limit.toString())
|
result = limit(result, query.limit.toString())
|
||||||
}
|
}
|
||||||
const response: SearchResponse<Record<string, any>> = { rows: result }
|
const response: SearchResponse<T> = { rows: result }
|
||||||
if (query.countRows) {
|
if (query.countRows) {
|
||||||
response.totalRows = totalRows
|
response.totalRows = totalRows
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
SearchFilters,
|
SearchFilters,
|
||||||
BasicOperator,
|
BasicOperator,
|
||||||
ArrayOperator,
|
ArrayOperator,
|
||||||
|
isLogicalSearchOperator,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import * as Constants from "./constants"
|
import * as Constants from "./constants"
|
||||||
import { removeKeyNumbering } from "./filters"
|
import { removeKeyNumbering } from "./filters"
|
||||||
|
@ -97,10 +98,20 @@ export function isSupportedUserSearch(query: SearchFilters) {
|
||||||
{ op: BasicOperator.EQUAL, key: "_id" },
|
{ op: BasicOperator.EQUAL, key: "_id" },
|
||||||
{ op: ArrayOperator.ONE_OF, 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") {
|
if (typeof operation !== "object") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLogicalSearchOperator(key)) {
|
||||||
|
for (const condition of query[key]!.conditions) {
|
||||||
|
if (!isSupportedUserSearch(condition)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const fields = Object.keys(operation || {})
|
const fields = Object.keys(operation || {})
|
||||||
// this filter doesn't contain options - ignore
|
// this filter doesn't contain options - ignore
|
||||||
if (fields.length === 0) {
|
if (fields.length === 0) {
|
||||||
|
|
|
@ -741,6 +741,25 @@ describe("/api/global/users", () => {
|
||||||
it("should throw an error if public query performed", async () => {
|
it("should throw an error if public query performed", async () => {
|
||||||
await config.api.users.searchUsers({}, { status: 403, noHeaders: true })
|
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", () => {
|
describe("DELETE /api/global/users/:userId", () => {
|
||||||
|
|
Loading…
Reference in New Issue