Bindings support for views and table row searches

This commit is contained in:
Dean 2024-04-18 17:04:26 +01:00
parent 5614c040ea
commit 6bbdf0e474
6 changed files with 122 additions and 41 deletions

View File

@ -1,7 +1,9 @@
<script>
import { createEventDispatcher } from "svelte"
import { ActionButton, Modal, ModalContent } from "@budibase/bbui"
import { ActionButton, Drawer, Button } from "@budibase/bbui"
import FilterDrawer from "components/design/settings/controls/FilterEditor/FilterDrawer.svelte"
import { getUserBindings } from "dataBinding"
import { makePropSafe } from "@budibase/string-templates"
export let schema
export let filters
@ -10,7 +12,7 @@
const dispatch = createEventDispatcher()
let modal
let drawer
$: tempValue = filters || []
$: schemaFields = Object.entries(schema || {}).map(
@ -22,37 +24,52 @@
$: text = getText(filters)
$: selected = tempValue.filter(x => !x.onEmptyFilter)?.length > 0
$: bindings = [
{
type: "context",
runtimeBinding: `${makePropSafe("now")}`,
readableBinding: `Date`,
category: "Date",
icon: "Date",
display: {
name: "Server date",
},
},
...getUserBindings(true),
]
const getText = filters => {
const count = filters?.filter(filter => filter.field)?.length
return count ? `Filter (${count})` : "Filter"
}
</script>
<ActionButton icon="Filter" quiet {disabled} on:click={modal.show} {selected}>
<ActionButton icon="Filter" quiet {disabled} on:click={drawer.show} {selected}>
{text}
</ActionButton>
<Modal bind:this={modal}>
<ModalContent
title="Filter"
confirmText="Save"
size="XL"
onConfirm={() => dispatch("change", tempValue)}
>
<div class="wrapper">
<FilterDrawer
allowBindings={false}
{filters}
{schemaFields}
datasource={{ type: "table", tableId }}
on:change={e => (tempValue = e.detail)}
/>
</div>
</ModalContent>
</Modal>
<style>
.wrapper :global(.main) {
padding: 0;
}
</style>
<Drawer
bind:this={drawer}
title="Filtering"
on:drawerHide
on:drawerShow
forceModal
>
<Button
cta
slot="buttons"
on:click={() => {
dispatch("change", tempValue)
drawer.hide()
}}
>
Save
</Button>
<FilterDrawer
slot="body"
{filters}
{schemaFields}
datasource={{ type: "table", tableId }}
on:change={e => (tempValue = e.detail)}
{bindings}
/>
</Drawer>

View File

@ -304,6 +304,7 @@
OperatorOptions.ContainsAny.value,
].includes(filter.operator)}
disabled={filter.noValue}
type={filter.valueType}
/>
{:else}
<DrawerBindableInput disabled />

View File

@ -1,7 +1,6 @@
<script>
import { Select, Multiselect } from "@budibase/bbui"
import { fetchData } from "@budibase/frontend-core"
import { API } from "api"
export let value = null
@ -23,12 +22,14 @@
$: component = multiselect ? Multiselect : Select
</script>
<svelte:component
this={component}
bind:value
autocomplete
{options}
getOptionLabel={option => option.email}
getOptionValue={option => option._id}
{disabled}
/>
<div class="user-control">
<svelte:component
this={component}
bind:value
autocomplete
{options}
getOptionLabel={option => option.email}
getOptionValue={option => option._id}
{disabled}
/>
</div>

View File

@ -2,7 +2,7 @@ import stream from "stream"
import archiver from "archiver"
import { quotas } from "@budibase/pro"
import { objectStore } from "@budibase/backend-core"
import { objectStore, context } from "@budibase/backend-core"
import * as internal from "./internal"
import * as external from "./external"
import { isExternalTableID } from "../../../integrations/utils"
@ -198,8 +198,21 @@ export async function destroy(ctx: UserCtx<DeleteRowRequest>) {
export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
const tableId = utils.getTableId(ctx)
// Current user context for bindable search
const { _id, _rev, firstName, lastName, email, status, roleId } = ctx.user
await context.ensureSnippetContext()
const enrichedQuery = await utils.enrichSearchContext(
{ ...ctx.request.body.query },
{
user: { _id, _rev, firstName, lastName, email, status, roleId },
}
)
const searchParams: RowSearchParams = {
...ctx.request.body,
query: enrichedQuery,
tableId,
}

View File

@ -7,6 +7,8 @@ import {
FieldType,
RelationshipsJson,
Row,
SearchRowRequest,
SearchRowResponse,
Table,
UserCtx,
} from "@budibase/types"
@ -22,7 +24,7 @@ import {
getInternalRowId,
} from "./basic"
import sdk from "../../../../sdk"
import { processStringSync } from "@budibase/string-templates"
import validateJs from "validate.js"
validateJs.extend(validateJs.validators.datetime, {
@ -187,3 +189,40 @@ export async function sqlOutputProcessing(
export function isUserMetadataTable(tableId: string) {
return tableId === InternalTables.USER_METADATA
}
export async function enrichSearchContext(
fields: Record<string, any>,
inputs = {},
helpers = true
): Promise<Record<string, any>> {
const enrichedQuery: Record<string, any> = {}
if (!fields || !inputs) {
return enrichedQuery
}
const parameters = { ...inputs }
// enrich the fields with dynamic parameters
for (let key of Object.keys(fields)) {
if (fields[key] == null) {
continue
}
if (typeof fields[key] === "object") {
// enrich nested fields object
enrichedQuery[key] = await enrichSearchContext(
fields[key],
parameters,
helpers
)
} else if (typeof fields[key] === "string") {
// enrich string value as normal
enrichedQuery[key] = processStringSync(fields[key], parameters, {
noEscaping: true,
noHelpers: !helpers,
escapeNewlines: true,
})
} else {
enrichedQuery[key] = fields[key]
}
}
return enrichedQuery
}

View File

@ -9,7 +9,8 @@ import {
} from "@budibase/types"
import { dataFilters } from "@budibase/shared-core"
import sdk from "../../../sdk"
import { db } from "@budibase/backend-core"
import { db, context } from "@budibase/backend-core"
import { enrichSearchContext, userSearchFromContext } from "./utils"
export async function searchView(
ctx: UserCtx<SearchViewRowRequest, SearchRowResponse>
@ -56,10 +57,19 @@ export async function searchView(
})
}
// Current user search context.
const { _id, _rev, firstName, lastName, email, status, roleId } = ctx.user
await context.ensureSnippetContext()
const enrichedQuery = await enrichSearchContext(query, {
user: { _id, _rev, firstName, lastName, email, status, roleId },
})
const searchOptions: RequiredKeys<SearchViewRowRequest> &
RequiredKeys<Pick<RowSearchParams, "tableId" | "query" | "fields">> = {
tableId: view.tableId,
query,
query: enrichedQuery,
fields: viewFields,
...getSortOptions(body, view),
limit: body.limit,