Making more progress on testing view searching.
This commit is contained in:
parent
b3b7069deb
commit
714029b9a0
|
@ -29,12 +29,18 @@ import {
|
||||||
JsonTypes,
|
JsonTypes,
|
||||||
FilterGroupLogicalOperator,
|
FilterGroupLogicalOperator,
|
||||||
EmptyFilterOption,
|
EmptyFilterOption,
|
||||||
|
JsonFieldSubType,
|
||||||
|
SearchFilterGroup,
|
||||||
|
LegacyFilter,
|
||||||
|
SearchViewRowRequest,
|
||||||
|
SearchFilterChild,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { generator, mocks } from "@budibase/backend-core/tests"
|
import { generator, mocks } from "@budibase/backend-core/tests"
|
||||||
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
|
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
|
||||||
import merge from "lodash/merge"
|
import merge from "lodash/merge"
|
||||||
import { quotas } from "@budibase/pro"
|
import { quotas } from "@budibase/pro"
|
||||||
import { db, roles, features } from "@budibase/backend-core"
|
import { db, roles, features } from "@budibase/backend-core"
|
||||||
|
import { single } from "validate.js"
|
||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
["lucene", undefined],
|
["lucene", undefined],
|
||||||
|
@ -3657,6 +3663,148 @@ describe.each([
|
||||||
expect(rows).toHaveLength(1)
|
expect(rows).toHaveLength(1)
|
||||||
expect(rows[0].user._id).toEqual(config.getUser()._id)
|
expect(rows[0].user._id).toEqual(config.getUser()._id)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("search operators", () => {
|
||||||
|
let table: Table
|
||||||
|
beforeEach(async () => {
|
||||||
|
table = await config.api.table.save(
|
||||||
|
saveTableRequest({
|
||||||
|
schema: {
|
||||||
|
string: { name: "string", type: FieldType.STRING },
|
||||||
|
longform: { name: "longform", type: FieldType.LONGFORM },
|
||||||
|
options: {
|
||||||
|
name: "options",
|
||||||
|
type: FieldType.OPTIONS,
|
||||||
|
constraints: { inclusion: ["a", "b", "c"] },
|
||||||
|
},
|
||||||
|
array: {
|
||||||
|
name: "array",
|
||||||
|
type: FieldType.ARRAY,
|
||||||
|
constraints: {
|
||||||
|
type: JsonFieldSubType.ARRAY,
|
||||||
|
inclusion: ["a", "b", "c"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
number: { name: "number", type: FieldType.NUMBER },
|
||||||
|
bigint: { name: "bigint", type: FieldType.BIGINT },
|
||||||
|
datetime: { name: "datetime", type: FieldType.DATETIME },
|
||||||
|
timeOnly: {
|
||||||
|
name: "timeOnly",
|
||||||
|
type: FieldType.DATETIME,
|
||||||
|
timeOnly: true,
|
||||||
|
},
|
||||||
|
boolean: { name: "boolean", type: FieldType.BOOLEAN },
|
||||||
|
user: {
|
||||||
|
name: "user",
|
||||||
|
type: FieldType.BB_REFERENCE_SINGLE,
|
||||||
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
name: "users",
|
||||||
|
type: FieldType.BB_REFERENCE,
|
||||||
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
interface TestCase {
|
||||||
|
name: string
|
||||||
|
query: SearchFilterGroup
|
||||||
|
insert: Row[]
|
||||||
|
expected: Row[]
|
||||||
|
searchOpts?: SearchViewRowRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultQuery(
|
||||||
|
query: Partial<SearchFilterGroup>
|
||||||
|
): SearchFilterGroup {
|
||||||
|
return {
|
||||||
|
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||||
|
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||||
|
groups: [],
|
||||||
|
...query,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultGroup(
|
||||||
|
group: Partial<SearchFilterChild>
|
||||||
|
): SearchFilterChild {
|
||||||
|
return {
|
||||||
|
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||||
|
filters: [],
|
||||||
|
...group,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function simpleQuery(...filters: LegacyFilter[]): SearchFilterGroup {
|
||||||
|
return defaultQuery({ groups: [defaultGroup({ filters })] })
|
||||||
|
}
|
||||||
|
|
||||||
|
const testCases: TestCase[] = [
|
||||||
|
{
|
||||||
|
name: "empty query return all",
|
||||||
|
insert: [{ string: "foo" }],
|
||||||
|
query: defaultQuery({
|
||||||
|
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||||
|
}),
|
||||||
|
expected: [{ string: "foo" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty query return none",
|
||||||
|
insert: [{ string: "foo" }],
|
||||||
|
query: defaultQuery({
|
||||||
|
onEmptyFilter: EmptyFilterOption.RETURN_NONE,
|
||||||
|
}),
|
||||||
|
expected: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple string search",
|
||||||
|
insert: [{ string: "foo" }],
|
||||||
|
query: simpleQuery({
|
||||||
|
operator: BasicOperator.EQUAL,
|
||||||
|
field: "string",
|
||||||
|
value: "foo",
|
||||||
|
}),
|
||||||
|
expected: [{ string: "foo" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non matching string search",
|
||||||
|
insert: [{ string: "foo" }],
|
||||||
|
query: simpleQuery({
|
||||||
|
operator: BasicOperator.EQUAL,
|
||||||
|
field: "string",
|
||||||
|
value: "bar",
|
||||||
|
}),
|
||||||
|
expected: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
it.only.each(testCases)(
|
||||||
|
"$name",
|
||||||
|
async ({ query, insert, expected, searchOpts }) => {
|
||||||
|
await config.api.row.bulkImport(table._id!, { rows: insert })
|
||||||
|
|
||||||
|
const view = await config.api.viewV2.create({
|
||||||
|
tableId: table._id!,
|
||||||
|
name: generator.guid(),
|
||||||
|
queryUI: query,
|
||||||
|
schema: {
|
||||||
|
string: { visible: true },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const { rows } = await config.api.viewV2.search(
|
||||||
|
view.id,
|
||||||
|
searchOpts
|
||||||
|
)
|
||||||
|
expect(rows).toEqual(
|
||||||
|
expected.map(r => expect.objectContaining(r))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("permissions", () => {
|
describe("permissions", () => {
|
||||||
|
|
|
@ -89,9 +89,6 @@ export async function search(
|
||||||
options = searchInputMapping(table, options)
|
options = searchInputMapping(table, options)
|
||||||
|
|
||||||
if (options.viewId) {
|
if (options.viewId) {
|
||||||
// Delete extraneous search params that cannot be overridden
|
|
||||||
delete options.query.onEmptyFilter
|
|
||||||
|
|
||||||
const view = source as ViewV2
|
const view = source as ViewV2
|
||||||
// Enrich saved query with ephemeral query params.
|
// Enrich saved query with ephemeral query params.
|
||||||
// We prevent searching on any fields that are saved as part of the query, as
|
// We prevent searching on any fields that are saved as part of the query, as
|
||||||
|
@ -99,7 +96,6 @@ export async function search(
|
||||||
let viewQuery = await enrichSearchContext(view.query || {}, context)
|
let viewQuery = await enrichSearchContext(view.query || {}, context)
|
||||||
viewQuery = dataFilters.buildQueryLegacy(viewQuery) || {}
|
viewQuery = dataFilters.buildQueryLegacy(viewQuery) || {}
|
||||||
viewQuery = checkFilters(table, viewQuery)
|
viewQuery = checkFilters(table, viewQuery)
|
||||||
delete viewQuery?.onEmptyFilter
|
|
||||||
|
|
||||||
const sqsEnabled = await features.flags.isEnabled("SQS")
|
const sqsEnabled = await features.flags.isEnabled("SQS")
|
||||||
const supportsLogicalOperators =
|
const supportsLogicalOperators =
|
||||||
|
@ -112,8 +108,6 @@ export async function search(
|
||||||
? view.query
|
? view.query
|
||||||
: []
|
: []
|
||||||
|
|
||||||
delete options.query.onEmptyFilter
|
|
||||||
|
|
||||||
// Extract existing fields
|
// Extract existing fields
|
||||||
const existingFields =
|
const existingFields =
|
||||||
queryFilters
|
queryFilters
|
||||||
|
|
|
@ -600,7 +600,7 @@ export function buildQuery(
|
||||||
|
|
||||||
const globalOperator = operatorMap[parsedFilter.logicalOperator]
|
const globalOperator = operatorMap[parsedFilter.logicalOperator]
|
||||||
|
|
||||||
return {
|
const ret = {
|
||||||
...(globalOnEmpty ? { onEmptyFilter: globalOnEmpty } : {}),
|
...(globalOnEmpty ? { onEmptyFilter: globalOnEmpty } : {}),
|
||||||
[globalOperator]: {
|
[globalOperator]: {
|
||||||
conditions: parsedFilter.groups?.map(group => {
|
conditions: parsedFilter.groups?.map(group => {
|
||||||
|
@ -614,6 +614,8 @@ export function buildQuery(
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// The frontend can send single values for array fields sometimes, so to handle
|
// The frontend can send single values for array fields sometimes, so to handle
|
||||||
|
|
Loading…
Reference in New Issue