Skip to content

Commit

Permalink
Making more progress on testing view searching.
Browse files Browse the repository at this point in the history
  • Loading branch information
samwho committed Oct 17, 2024
1 parent b3b7069 commit 714029b
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 7 deletions.
148 changes: 148 additions & 0 deletions packages/server/src/api/routes/tests/viewV2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,18 @@ import {
JsonTypes,
FilterGroupLogicalOperator,
EmptyFilterOption,
JsonFieldSubType,
SearchFilterGroup,
LegacyFilter,
SearchViewRowRequest,
SearchFilterChild,
} from "@budibase/types"
import { generator, mocks } from "@budibase/backend-core/tests"
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
import merge from "lodash/merge"
import { quotas } from "@budibase/pro"
import { db, roles, features } from "@budibase/backend-core"
import { single } from "validate.js"

Check failure on line 43 in packages/server/src/api/routes/tests/viewV2.spec.ts

View workflow job for this annotation

GitHub Actions / lint

'single' is defined but never used. Allowed unused vars must match /^_/u

describe.each([
["lucene", undefined],
Expand Down Expand Up @@ -3657,6 +3663,148 @@ describe.each([
expect(rows).toHaveLength(1)
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)(

Check failure on line 3784 in packages/server/src/api/routes/tests/viewV2.spec.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected focused test
"$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", () => {
Expand Down
6 changes: 0 additions & 6 deletions packages/server/src/sdk/app/rows/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,13 @@ export async function search(
options = searchInputMapping(table, options)

if (options.viewId) {
// Delete extraneous search params that cannot be overridden
delete options.query.onEmptyFilter

const view = source as ViewV2
// Enrich saved query with ephemeral query params.
// We prevent searching on any fields that are saved as part of the query, as
// that could let users find rows they should not be allowed to access.
let viewQuery = await enrichSearchContext(view.query || {}, context)
viewQuery = dataFilters.buildQueryLegacy(viewQuery) || {}
viewQuery = checkFilters(table, viewQuery)
delete viewQuery?.onEmptyFilter

const sqsEnabled = await features.flags.isEnabled("SQS")
const supportsLogicalOperators =
Expand All @@ -112,8 +108,6 @@ export async function search(
? view.query
: []

delete options.query.onEmptyFilter

// Extract existing fields
const existingFields =
queryFilters
Expand Down
4 changes: 3 additions & 1 deletion packages/shared-core/src/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ export function buildQuery(

const globalOperator = operatorMap[parsedFilter.logicalOperator]

return {
const ret = {
...(globalOnEmpty ? { onEmptyFilter: globalOnEmpty } : {}),
[globalOperator]: {
conditions: parsedFilter.groups?.map(group => {
Expand All @@ -614,6 +614,8 @@ export function buildQuery(
}),
},
}

return ret
}

// The frontend can send single values for array fields sometimes, so to handle
Expand Down

0 comments on commit 714029b

Please sign in to comment.