Move query logic to sdk
This commit is contained in:
parent
26638ace0a
commit
6e1cd6eb01
|
@ -3,14 +3,9 @@ import {
|
|||
ViewV2,
|
||||
SearchRowResponse,
|
||||
SearchViewRowRequest,
|
||||
SearchFilterKey,
|
||||
LogicalOperator,
|
||||
} from "@budibase/types"
|
||||
import { dataFilters } from "@budibase/shared-core"
|
||||
import sdk from "../../../sdk"
|
||||
import { db, context, features } from "@budibase/backend-core"
|
||||
import { enrichSearchContext } from "./utils"
|
||||
import { isExternalTableID } from "../../../integrations/utils"
|
||||
import { context } from "@budibase/backend-core"
|
||||
|
||||
export async function searchView(
|
||||
ctx: UserCtx<SearchViewRowRequest, SearchRowResponse>
|
||||
|
@ -27,58 +22,23 @@ export async function searchView(
|
|||
|
||||
const { body } = ctx.request
|
||||
|
||||
// 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 query = dataFilters.buildQuery(view.query || [])
|
||||
if (body.query) {
|
||||
// Delete extraneous search params that cannot be overridden
|
||||
delete body.query.onEmptyFilter
|
||||
|
||||
if (
|
||||
!isExternalTableID(view.tableId) &&
|
||||
!(await features.flags.isEnabled("SQS"))
|
||||
) {
|
||||
// Extract existing fields
|
||||
const existingFields =
|
||||
view.query
|
||||
?.filter(filter => filter.field)
|
||||
.map(filter => db.removeKeyNumbering(filter.field)) || []
|
||||
|
||||
// Carry over filters for unused fields
|
||||
Object.keys(body.query).forEach(key => {
|
||||
const operator = key as Exclude<SearchFilterKey, LogicalOperator>
|
||||
Object.keys(body.query[operator] || {}).forEach(field => {
|
||||
if (!existingFields.includes(db.removeKeyNumbering(field))) {
|
||||
query[operator]![field] = body.query[operator]![field]
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
query = {
|
||||
$and: {
|
||||
conditions: [query, body.query],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await context.ensureSnippetContext(true)
|
||||
|
||||
const enrichedQuery = await enrichSearchContext(query, {
|
||||
user: sdk.users.getUserContextBindings(ctx.user),
|
||||
})
|
||||
|
||||
const result = await sdk.rows.search({
|
||||
viewId: view.id,
|
||||
tableId: view.tableId,
|
||||
query: enrichedQuery,
|
||||
...getSortOptions(body, view),
|
||||
limit: body.limit,
|
||||
bookmark: body.bookmark,
|
||||
paginate: body.paginate,
|
||||
countRows: body.countRows,
|
||||
})
|
||||
const result = await sdk.rows.search(
|
||||
{
|
||||
viewId: view.id,
|
||||
tableId: view.tableId,
|
||||
query: body.query,
|
||||
...getSortOptions(body, view),
|
||||
limit: body.limit,
|
||||
bookmark: body.bookmark,
|
||||
paginate: body.paginate,
|
||||
countRows: body.countRows,
|
||||
},
|
||||
{
|
||||
user: sdk.users.getUserContextBindings(ctx.user),
|
||||
}
|
||||
)
|
||||
|
||||
result.rows.forEach(r => (r._viewId = view.id))
|
||||
ctx.body = result
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import {
|
||||
EmptyFilterOption,
|
||||
LogicalOperator,
|
||||
Row,
|
||||
RowSearchParams,
|
||||
SearchFilterKey,
|
||||
SearchFilters,
|
||||
SearchResponse,
|
||||
SortOrder,
|
||||
Table,
|
||||
|
@ -14,9 +17,10 @@ import { ExportRowsParams, ExportRowsResult } from "./search/types"
|
|||
import { dataFilters } from "@budibase/shared-core"
|
||||
import sdk from "../../index"
|
||||
import { searchInputMapping } from "./search/utils"
|
||||
import { features } from "@budibase/backend-core"
|
||||
import { db, features } from "@budibase/backend-core"
|
||||
import tracer from "dd-trace"
|
||||
import { getQueryableFields, removeInvalidFilters } from "./queryUtils"
|
||||
import { enrichSearchContext } from "../../../api/controllers/row/utils"
|
||||
|
||||
export { isValidFilter } from "../../../integrations/utils"
|
||||
|
||||
|
@ -34,7 +38,8 @@ function pickApi(tableId: any) {
|
|||
}
|
||||
|
||||
export async function search(
|
||||
options: RowSearchParams
|
||||
options: RowSearchParams,
|
||||
context?: Record<string, any>
|
||||
): Promise<SearchResponse<Row>> {
|
||||
return await tracer.trace("search", async span => {
|
||||
span?.addTags({
|
||||
|
@ -51,6 +56,69 @@ export async function search(
|
|||
countRows: options.countRows,
|
||||
})
|
||||
|
||||
let source: Table | ViewV2
|
||||
let table: Table
|
||||
if (options.viewId) {
|
||||
source = await sdk.views.get(options.viewId)
|
||||
table = await sdk.views.getTable(source)
|
||||
options = searchInputMapping(table, options)
|
||||
} else if (options.tableId) {
|
||||
source = await sdk.tables.getTable(options.tableId)
|
||||
table = source
|
||||
} else {
|
||||
throw new Error(`Must supply either a view ID or a table ID`)
|
||||
}
|
||||
|
||||
if (options.query) {
|
||||
const visibleFields = (
|
||||
options.fields || Object.keys(table.schema)
|
||||
).filter(field => table.schema[field].visible !== false)
|
||||
|
||||
const queryableFields = await getQueryableFields(table, visibleFields)
|
||||
options.query = removeInvalidFilters(options.query, queryableFields)
|
||||
}
|
||||
|
||||
if (options.viewId) {
|
||||
const view = await sdk.views.get(options.viewId)
|
||||
// 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.buildQuery(view.query || [])
|
||||
|
||||
if (!isExternalTable && !(await features.flags.isEnabled("SQS"))) {
|
||||
// Lucene does not accept conditional filters, so we need to keep the old logic
|
||||
const query: SearchFilters = {}
|
||||
|
||||
// Extract existing fields
|
||||
const existingFields =
|
||||
view.query
|
||||
?.filter(filter => filter.field)
|
||||
.map(filter => db.removeKeyNumbering(filter.field)) || []
|
||||
|
||||
// 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))) {
|
||||
query[operator]![field] = viewQuery[operator]![field]
|
||||
} else {
|
||||
query[operator]![field] = options.query[operator]![field]
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
options.query = {
|
||||
$and: {
|
||||
conditions: [viewQuery, options.query],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context) {
|
||||
options.query = await enrichSearchContext(options.query, context)
|
||||
}
|
||||
|
||||
options.query = dataFilters.cleanupQuery(options.query || {})
|
||||
options.query = dataFilters.fixupFilterArrays(options.query)
|
||||
|
||||
|
@ -72,28 +140,7 @@ export async function search(
|
|||
options.sortOrder = options.sortOrder.toLowerCase() as SortOrder
|
||||
}
|
||||
|
||||
let source: Table | ViewV2
|
||||
let table: Table
|
||||
if (options.viewId) {
|
||||
source = await sdk.views.get(options.viewId)
|
||||
table = await sdk.views.getTable(source)
|
||||
options = searchInputMapping(table, options)
|
||||
} else if (options.tableId) {
|
||||
source = await sdk.tables.getTable(options.tableId)
|
||||
table = source
|
||||
options = searchInputMapping(table, options)
|
||||
} else {
|
||||
throw new Error(`Must supply either a view ID or a table ID`)
|
||||
}
|
||||
|
||||
if (options.query) {
|
||||
const visibleFields = (
|
||||
options.fields || Object.keys(table.schema)
|
||||
).filter(field => table.schema[field].visible !== false)
|
||||
|
||||
const queryableFields = await getQueryableFields(table, visibleFields)
|
||||
options.query = removeInvalidFilters(options.query, queryableFields)
|
||||
}
|
||||
options = searchInputMapping(table, options)
|
||||
|
||||
const isExternalTable = isExternalTableID(table._id!)
|
||||
let result: SearchResponse<Row>
|
||||
|
|
Loading…
Reference in New Issue