Merge branch 'master' into fix/screen-role-creation
This commit is contained in:
commit
c8f7722f9c
|
@ -3546,6 +3546,50 @@ describe.each([
|
|||
expect(response.rows).toHaveLength(1)
|
||||
expect(response.rows[0].sum).toEqual(61)
|
||||
})
|
||||
|
||||
it("should be able to filter on a single user field in both the view query and search query", async () => {
|
||||
const table = await config.api.table.save(
|
||||
saveTableRequest({
|
||||
schema: {
|
||||
user: {
|
||||
name: "user",
|
||||
type: FieldType.BB_REFERENCE_SINGLE,
|
||||
subtype: BBReferenceFieldSubType.USER,
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
await config.api.row.save(table._id!, {
|
||||
user: config.getUser()._id,
|
||||
})
|
||||
|
||||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
query: {
|
||||
equal: {
|
||||
user: "{{ [user].[_id] }}",
|
||||
},
|
||||
},
|
||||
schema: {
|
||||
user: {
|
||||
visible: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const { rows } = await config.api.viewV2.search(view.id, {
|
||||
query: {
|
||||
equal: {
|
||||
user: "{{ [user].[_id] }}",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(rows).toHaveLength(1)
|
||||
expect(rows[0].user._id).toEqual(config.getUser()._id)
|
||||
})
|
||||
})
|
||||
|
||||
describe("permissions", () => {
|
||||
|
|
|
@ -16,7 +16,7 @@ import * as external from "./search/external"
|
|||
import { ExportRowsParams, ExportRowsResult } from "./search/types"
|
||||
import { dataFilters } from "@budibase/shared-core"
|
||||
import sdk from "../../index"
|
||||
import { searchInputMapping } from "./search/utils"
|
||||
import { checkFilters, searchInputMapping } from "./search/utils"
|
||||
import { db, features } from "@budibase/backend-core"
|
||||
import tracer from "dd-trace"
|
||||
import { getQueryableFields, removeInvalidFilters } from "./queryUtils"
|
||||
|
@ -81,6 +81,10 @@ export async function search(
|
|||
options.query = {}
|
||||
}
|
||||
|
||||
if (context) {
|
||||
options.query = await enrichSearchContext(options.query, context)
|
||||
}
|
||||
|
||||
// need to make sure filters in correct shape before checking for view
|
||||
options = searchInputMapping(table, options)
|
||||
|
||||
|
@ -92,12 +96,15 @@ export async function search(
|
|||
// 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 = dataFilters.buildQueryLegacy(view.query) || {}
|
||||
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 =
|
||||
isExternalTableID(view.tableId) || sqsEnabled
|
||||
|
||||
if (!supportsLogicalOperators) {
|
||||
// In the unlikely event that a Grouped Filter is in a non-SQS environment
|
||||
// It needs to be ignored entirely
|
||||
|
@ -113,13 +120,12 @@ export async function search(
|
|||
?.filter(filter => filter.field)
|
||||
.map(filter => db.removeKeyNumbering(filter.field)) || []
|
||||
|
||||
viewQuery ??= {}
|
||||
// Carry over filters for unused fields
|
||||
Object.keys(options.query).forEach(key => {
|
||||
const operator = key as Exclude<SearchFilterKey, LogicalOperator>
|
||||
Object.keys(options.query[operator] || {}).forEach(field => {
|
||||
if (!existingFields.includes(db.removeKeyNumbering(field))) {
|
||||
viewQuery![operator]![field] = options.query[operator]![field]
|
||||
viewQuery[operator]![field] = options.query[operator]![field]
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -137,10 +143,6 @@ export async function search(
|
|||
}
|
||||
}
|
||||
|
||||
if (context) {
|
||||
options.query = await enrichSearchContext(options.query, context)
|
||||
}
|
||||
|
||||
options.query = dataFilters.cleanupQuery(options.query)
|
||||
options.query = dataFilters.fixupFilterArrays(options.query)
|
||||
|
||||
|
|
|
@ -80,35 +80,41 @@ function userColumnMapping(column: string, filters: SearchFilters) {
|
|||
})
|
||||
}
|
||||
|
||||
export function checkFilters(
|
||||
table: Table,
|
||||
filters: SearchFilters
|
||||
): SearchFilters {
|
||||
for (let [key, column] of Object.entries(table.schema || {})) {
|
||||
switch (column.type) {
|
||||
case FieldType.BB_REFERENCE_SINGLE: {
|
||||
const subtype = column.subtype
|
||||
switch (subtype) {
|
||||
case BBReferenceFieldSubType.USER:
|
||||
userColumnMapping(key, filters)
|
||||
break
|
||||
|
||||
default:
|
||||
utils.unreachable(subtype)
|
||||
}
|
||||
break
|
||||
}
|
||||
case FieldType.BB_REFERENCE: {
|
||||
userColumnMapping(key, filters)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataFilters.recurseLogicalOperators(filters, filters =>
|
||||
checkFilters(table, filters)
|
||||
)
|
||||
}
|
||||
|
||||
// maps through the search parameters to check if any of the inputs are invalid
|
||||
// based on the table schema, converts them to something that is valid.
|
||||
export function searchInputMapping(table: Table, options: RowSearchParams) {
|
||||
// need an internal function to loop over filters, because this takes the full options
|
||||
function checkFilters(filters: SearchFilters) {
|
||||
for (let [key, column] of Object.entries(table.schema || {})) {
|
||||
switch (column.type) {
|
||||
case FieldType.BB_REFERENCE_SINGLE: {
|
||||
const subtype = column.subtype
|
||||
switch (subtype) {
|
||||
case BBReferenceFieldSubType.USER:
|
||||
userColumnMapping(key, filters)
|
||||
break
|
||||
|
||||
default:
|
||||
utils.unreachable(subtype)
|
||||
}
|
||||
break
|
||||
}
|
||||
case FieldType.BB_REFERENCE: {
|
||||
userColumnMapping(key, filters)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataFilters.recurseLogicalOperators(filters, checkFilters)
|
||||
}
|
||||
if (options.query) {
|
||||
options.query = checkFilters(options.query)
|
||||
options.query = checkFilters(table, options.query)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue