diff --git a/.github/workflows/deploy-featurebranch.yml b/.github/workflows/deploy-featurebranch.yml
index d86d301507..0e19f0649f 100644
--- a/.github/workflows/deploy-featurebranch.yml
+++ b/.github/workflows/deploy-featurebranch.yml
@@ -3,26 +3,50 @@ name: deploy-featurebranch
on:
pull_request:
types: [
- labeled,
- # default types below (https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request)
- opened,
- synchronize,
- reopened,
- ]
+ labeled,
+ # default types below (https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request)
+ opened,
+ synchronize,
+ reopened,
+ ]
jobs:
release:
if: |
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase') &&
- contains(github.event.pull_request.labels.*.name, 'feature-branch')
+ (
+ contains(github.event.pull_request.labels.*.name, 'feature-branch') ||
+ contains(github.event.pull_request.labels.*.name, 'feature-branch-pro') ||
+ contains(github.event.pull_request.labels.*.name, 'feature-branch-team') ||
+ contains(github.event.pull_request.labels.*.name, 'feature-branch-business') ||
+ contains(github.event.pull_request.labels.*.name, 'feature-branch-enterprise')
+ )
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+
+ - name: Set PAYLOAD_LICENSE_TYPE
+ id: set_license_type
+ run: |
+ if [[ "${{ contains(github.event.pull_request.labels.*.name, 'feature-branch') }}" == "true" ]]; then
+ echo "PAYLOAD_LICENSE_TYPE=free" >> $GITHUB_ENV
+ elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'feature-branch-pro') }}" == "true" ]]; then
+ echo "PAYLOAD_LICENSE_TYPE=pro" >> $GITHUB_ENV
+ elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'feature-branch-team') }}" == "true" ]]; then
+ echo "PAYLOAD_LICENSE_TYPE=team" >> $GITHUB_ENV
+ elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'feature-branch-business') }}" == "true" ]]; then
+ echo "PAYLOAD_LICENSE_TYPE=business" >> $GITHUB_ENV
+ elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'feature-branch-enterprise') }}" == "true" ]]; then
+ echo "PAYLOAD_LICENSE_TYPE=enterprise" >> $GITHUB_ENV
+ else
+ echo "PAYLOAD_LICENSE_TYPE=free" >> $GITHUB_ENV
+ fi
+
- uses: passeidireto/trigger-external-workflow-action@main
env:
PAYLOAD_BRANCH: ${{ github.head_ref }}
PAYLOAD_PR_NUMBER: ${{ github.event.pull_request.number }}
- PAYLOAD_LICENSE_TYPE: "free"
+ PAYLOAD_LICENSE_TYPE: ${{ env.PAYLOAD_LICENSE_TYPE }}
with:
repository: budibase/budibase-deploys
event: featurebranch-qa-deploy
diff --git a/lerna.json b/lerna.json
index 4d36affd04..13530e9aee 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,6 +1,6 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
- "version": "2.32.15",
+ "version": "2.32.17",
"npmClient": "yarn",
"packages": [
"packages/*",
diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts
index f122ad1c41..382eca3f76 100644
--- a/packages/backend-core/src/sql/sql.ts
+++ b/packages/backend-core/src/sql/sql.ts
@@ -273,6 +273,7 @@ class InternalBuilder {
const col = parts.pop()!
const schema = this.table.schema[col]
let identifier = this.quotedIdentifier(field)
+
if (
schema.type === FieldType.STRING ||
schema.type === FieldType.LONGFORM ||
@@ -957,6 +958,13 @@ class InternalBuilder {
return query
}
+ isAggregateField(field: string): boolean {
+ const found = this.query.resource?.aggregations?.find(
+ aggregation => aggregation.name === field
+ )
+ return !!found
+ }
+
addSorting(query: Knex.QueryBuilder): Knex.QueryBuilder {
let { sort, resource } = this.query
const primaryKey = this.table.primary
@@ -979,13 +987,17 @@ class InternalBuilder {
nulls = value.direction === SortOrder.ASCENDING ? "first" : "last"
}
- let composite = `${aliased}.${key}`
- if (this.client === SqlClient.ORACLE) {
- query = query.orderByRaw(
- `${this.convertClobs(composite)} ${direction} nulls ${nulls}`
- )
+ if (this.isAggregateField(key)) {
+ query = query.orderBy(key, direction, nulls)
} else {
- query = query.orderBy(composite, direction, nulls)
+ let composite = `${aliased}.${key}`
+ if (this.client === SqlClient.ORACLE) {
+ query = query.orderByRaw(
+ `${this.convertClobs(composite)} ${direction} nulls ${nulls}`
+ )
+ } else {
+ query = query.orderBy(composite, direction, nulls)
+ }
}
}
}
diff --git a/packages/builder/src/components/portal/licensing/EnterpriseBasicTrialBanner.svelte b/packages/builder/src/components/portal/licensing/EnterpriseBasicTrialBanner.svelte
index 111f0481b9..350ebb0f11 100644
--- a/packages/builder/src/components/portal/licensing/EnterpriseBasicTrialBanner.svelte
+++ b/packages/builder/src/components/portal/licensing/EnterpriseBasicTrialBanner.svelte
@@ -14,7 +14,13 @@
function daysUntilCancel() {
const cancelAt = license?.billing?.subscription?.cancelAt
const diffTime = Math.abs(cancelAt - new Date().getTime()) / 1000
- return Math.floor(diffTime / oneDayInSeconds)
+ const days = Math.floor(diffTime / oneDayInSeconds)
+ if (days === 1) {
+ return "tomorrow."
+ } else if (days === 0) {
+ return "today."
+ }
+ return `in ${days} days.`
}
@@ -28,7 +34,7 @@
extraLinkAction={$licensing.goToUpgradePage}
showCloseButton={false}
>
- Your free trial will end in {daysUntilCancel()} days.
+ Your free trial will end {daysUntilCancel()}
{/if}
diff --git a/packages/client/src/components/app/container/GridContainer.svelte b/packages/client/src/components/app/container/GridContainer.svelte
index 4dc75f3513..696b65abcd 100644
--- a/packages/client/src/components/app/container/GridContainer.svelte
+++ b/packages/client/src/components/app/container/GridContainer.svelte
@@ -4,6 +4,8 @@
import { GridRowHeight, GridColumns } from "constants"
import { memo } from "@budibase/frontend-core"
+ export let onClick
+
const component = getContext("component")
const { styleable, builderStore } = getContext("sdk")
const context = getContext("context")
@@ -121,15 +123,19 @@
})
+
+
{#if inBuilder}
@@ -176,6 +182,9 @@
.placeholder.first-col {
border-left: 1px solid var(--spectrum-global-color-gray-900);
}
+ .clickable {
+ cursor: pointer;
+ }
/* Highlight grid lines when resizing children */
:global(.grid.highlight > .underlay) {
diff --git a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte
index 73c8a99cc2..8f773d3f90 100644
--- a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte
+++ b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte
@@ -27,9 +27,7 @@
let candidateIndex
let lastSearchId
let searching = false
- let container
let anchor
- let relationshipFields
$: fieldValue = parseValue(value)
$: oneRowOnly = schema?.relationshipType === "one-to-many"
@@ -56,12 +54,6 @@
return acc
}, {})
- $: showRelationshipFields =
- relationshipFields &&
- Object.keys(relationshipFields).length &&
- focused &&
- !isOpen
-
const parseValue = value => {
if (Array.isArray(value) && value.every(x => x?._id)) {
return value
@@ -242,14 +234,6 @@
return value
}
- const displayRelationshipFields = relationship => {
- relationshipFields = relationFields[relationship._id]
- }
-
- const hideRelationshipFields = () => {
- relationshipFields = undefined
- }
-
onMount(() => {
api = {
focus: open,
@@ -269,7 +253,7 @@
style="--color:{color};"
bind:this={anchor}
>
-
+
1}
@@ -281,9 +265,7 @@
displayRelationshipFields(relationship)}
on:focus={() => {}}
- on:mouseleave={() => hideRelationshipFields()}
>
{readable(
@@ -358,21 +340,6 @@
{/if}
-{#if showRelationshipFields}
-
-
- {#each Object.entries(relationshipFields) as [fieldName, fieldValue]}
-
- {fieldName}
-
-
- {fieldValue}
-
- {/each}
-
-
-{/if}
-
diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte
index bbc3d55f04..dd12af3ff4 100644
--- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte
+++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte
@@ -125,7 +125,7 @@
subtype: column.subtype,
visible: column.visible,
readonly: column.readonly,
- constraints: column.constraints, // This is needed to properly display "users" column
+ icon: column.icon,
},
}
})
diff --git a/packages/frontend-core/src/components/grid/lib/utils.js b/packages/frontend-core/src/components/grid/lib/utils.js
index 1988b66cc2..fb062cb1fa 100644
--- a/packages/frontend-core/src/components/grid/lib/utils.js
+++ b/packages/frontend-core/src/components/grid/lib/utils.js
@@ -19,6 +19,10 @@ export const getCellID = (rowId, fieldName) => {
}
export const getColumnIcon = column => {
+ if (column.schema.icon) {
+ return column.schema.icon
+ }
+
if (column.schema.autocolumn) {
return "MagicWand"
}
diff --git a/packages/pro b/packages/pro
index aca9828117..fc4c7f4925 160000
--- a/packages/pro
+++ b/packages/pro
@@ -1 +1 @@
-Subproject commit aca9828117bb97f54f40ee359f1a3f6e259174e7
+Subproject commit fc4c7f4925139af078480217965c3d6338dc0a7f
diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts
index b86ec38d08..6490b4770a 100644
--- a/packages/server/src/api/routes/tests/row.spec.ts
+++ b/packages/server/src/api/routes/tests/row.spec.ts
@@ -14,6 +14,7 @@ import {
InternalTable,
tenancy,
features,
+ utils,
} from "@budibase/backend-core"
import { quotas } from "@budibase/pro"
import {
@@ -757,6 +758,37 @@ describe.each([
})
})
+ describe("user column", () => {
+ beforeAll(async () => {
+ table = await config.api.table.save(
+ saveTableRequest({
+ schema: {
+ user: {
+ name: "user",
+ type: FieldType.BB_REFERENCE_SINGLE,
+ subtype: BBReferenceFieldSubType.USER,
+ default: "{{ [Current User]._id }}",
+ },
+ },
+ })
+ )
+ })
+
+ it("creates a new row with a default value successfully", async () => {
+ const row = await config.api.row.save(table._id!, {})
+ expect(row.user._id).toEqual(config.getUser()._id)
+ })
+
+ it("does not use default value if value specified", async () => {
+ const id = `us_${utils.newid()}`
+ await config.createUser({ _id: id })
+ const row = await config.api.row.save(table._id!, {
+ user: id,
+ })
+ expect(row.user._id).toEqual(id)
+ })
+ })
+
describe("bindings", () => {
describe("string column", () => {
beforeAll(async () => {
diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts
index d6c1651ef0..f7fe6a66d4 100644
--- a/packages/server/src/api/routes/tests/table.spec.ts
+++ b/packages/server/src/api/routes/tests/table.spec.ts
@@ -21,6 +21,7 @@ import {
ViewCalculation,
ViewV2Enriched,
RowExportFormat,
+ PermissionLevel,
} from "@budibase/types"
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
import * as setup from "./utilities"
@@ -191,6 +192,55 @@ describe.each([
)
})
+ describe("permissions", () => {
+ it("get the base permissions for the table", async () => {
+ const table = await config.api.table.save(
+ tableForDatasource(datasource, {
+ schema: {
+ name: {
+ type: FieldType.STRING,
+ name: "name",
+ },
+ },
+ })
+ )
+
+ // get the explicit permissions
+ const { permissions } = await config.api.permission.get(table._id!, {
+ status: 200,
+ })
+ const explicitPermissions = {
+ role: "ADMIN",
+ permissionType: "EXPLICIT",
+ }
+ expect(permissions.write).toEqual(explicitPermissions)
+ expect(permissions.read).toEqual(explicitPermissions)
+
+ // revoke the explicit permissions
+ for (let level of [PermissionLevel.WRITE, PermissionLevel.READ]) {
+ await config.api.permission.revoke(
+ {
+ roleId: permissions[level].role,
+ resourceId: table._id!,
+ level,
+ },
+ { status: 200 }
+ )
+ }
+
+ // check base permissions
+ const { permissions: basePermissions } = await config.api.permission.get(
+ table._id!,
+ {
+ status: 200,
+ }
+ )
+ const basePerms = { role: "BASIC", permissionType: "BASE" }
+ expect(basePermissions.write).toEqual(basePerms)
+ expect(basePermissions.read).toEqual(basePerms)
+ })
+ })
+
describe("update", () => {
it("updates a table", async () => {
const table = await config.api.table.save(
diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts
index 0b4237406f..7979fac555 100644
--- a/packages/server/src/api/routes/tests/viewV2.spec.ts
+++ b/packages/server/src/api/routes/tests/viewV2.spec.ts
@@ -3381,6 +3381,124 @@ describe.each([
expect(rows).toHaveLength(1)
expect(rows[0].sum).toEqual(3)
})
+
+ it("should be able to sort by group by field", async () => {
+ const table = await config.api.table.save(
+ saveTableRequest({
+ schema: {
+ quantity: {
+ type: FieldType.NUMBER,
+ name: "quantity",
+ },
+ price: {
+ type: FieldType.NUMBER,
+ name: "price",
+ },
+ },
+ })
+ )
+
+ const view = await config.api.viewV2.create({
+ tableId: table._id!,
+ name: generator.guid(),
+ type: ViewV2Type.CALCULATION,
+ schema: {
+ quantity: { visible: true },
+ sum: {
+ visible: true,
+ calculationType: CalculationType.SUM,
+ field: "price",
+ },
+ },
+ })
+
+ await config.api.row.bulkImport(table._id!, {
+ rows: [
+ {
+ quantity: 1,
+ price: 1,
+ },
+ {
+ quantity: 1,
+ price: 2,
+ },
+ {
+ quantity: 2,
+ price: 10,
+ },
+ ],
+ })
+
+ const { rows } = await config.api.viewV2.search(view.id, {
+ query: {},
+ sort: "quantity",
+ sortOrder: SortOrder.DESCENDING,
+ })
+
+ expect(rows).toEqual([
+ expect.objectContaining({ quantity: 2, sum: 10 }),
+ expect.objectContaining({ quantity: 1, sum: 3 }),
+ ])
+ })
+
+ it("should be able to sort by a calculation", async () => {
+ const table = await config.api.table.save(
+ saveTableRequest({
+ schema: {
+ quantity: {
+ type: FieldType.NUMBER,
+ name: "quantity",
+ },
+ price: {
+ type: FieldType.NUMBER,
+ name: "price",
+ },
+ },
+ })
+ )
+
+ await config.api.row.bulkImport(table._id!, {
+ rows: [
+ {
+ quantity: 1,
+ price: 1,
+ },
+ {
+ quantity: 1,
+ price: 2,
+ },
+ {
+ quantity: 2,
+ price: 10,
+ },
+ ],
+ })
+
+ const view = await config.api.viewV2.create({
+ tableId: table._id!,
+ name: generator.guid(),
+ type: ViewV2Type.CALCULATION,
+ schema: {
+ quantity: { visible: true },
+ sum: {
+ visible: true,
+ calculationType: CalculationType.SUM,
+ field: "price",
+ },
+ },
+ })
+
+ const { rows } = await config.api.viewV2.search(view.id, {
+ query: {},
+ sort: "sum",
+ sortOrder: SortOrder.DESCENDING,
+ })
+
+ expect(rows).toEqual([
+ expect.objectContaining({ quantity: 2, sum: 10 }),
+ expect.objectContaining({ quantity: 1, sum: 3 }),
+ ])
+ })
})
!isLucene &&
@@ -3428,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", () => {
diff --git a/packages/server/src/sdk/app/permissions/index.ts b/packages/server/src/sdk/app/permissions/index.ts
index 97af9ccc83..7c385864f4 100644
--- a/packages/server/src/sdk/app/permissions/index.ts
+++ b/packages/server/src/sdk/app/permissions/index.ts
@@ -73,8 +73,7 @@ export async function getResourcePerms(
p[level] = { role, type: PermissionSource.BASE }
return p
}, {})
- const result = Object.assign(basePermissions, permissions)
- return result
+ return Object.assign(basePermissions, permissions)
}
export async function getDependantResources(
diff --git a/packages/server/src/sdk/app/rows/search.ts b/packages/server/src/sdk/app/rows/search.ts
index f68f78f0bc..7ac3bb8ead 100644
--- a/packages/server/src/sdk/app/rows/search.ts
+++ b/packages/server/src/sdk/app/rows/search.ts
@@ -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
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)
diff --git a/packages/server/src/sdk/app/rows/search/internal/sqs.ts b/packages/server/src/sdk/app/rows/search/internal/sqs.ts
index ad323a8c12..916e20957b 100644
--- a/packages/server/src/sdk/app/rows/search/internal/sqs.ts
+++ b/packages/server/src/sdk/app/rows/search/internal/sqs.ts
@@ -418,13 +418,26 @@ export async function search(
if (params.sort) {
const sortField = table.schema[params.sort]
- const sortType =
- sortField.type === FieldType.NUMBER ? SortType.NUMBER : SortType.STRING
- request.sort = {
- [mapToUserColumn(sortField.name)]: {
- direction: params.sortOrder || SortOrder.ASCENDING,
- type: sortType as SortType,
- },
+ const isAggregateField = aggregations.some(agg => agg.name === params.sort)
+
+ if (isAggregateField) {
+ request.sort = {
+ [params.sort]: {
+ direction: params.sortOrder || SortOrder.ASCENDING,
+ type: SortType.NUMBER,
+ },
+ }
+ } else if (sortField) {
+ const sortType =
+ sortField.type === FieldType.NUMBER ? SortType.NUMBER : SortType.STRING
+ request.sort = {
+ [mapToUserColumn(sortField.name)]: {
+ direction: params.sortOrder || SortOrder.ASCENDING,
+ type: sortType as SortType,
+ },
+ }
+ } else {
+ throw new Error(`Unable to sort by ${params.sort}`)
}
}
diff --git a/packages/server/src/sdk/app/rows/search/utils.ts b/packages/server/src/sdk/app/rows/search/utils.ts
index 90303a6ca7..c7de7eef37 100644
--- a/packages/server/src/sdk/app/rows/search/utils.ts
+++ b/packages/server/src/sdk/app/rows/search/utils.ts
@@ -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
}
diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts
index 35f2a84b07..45004a861d 100644
--- a/packages/server/src/sdk/app/views/index.ts
+++ b/packages/server/src/sdk/app/views/index.ts
@@ -1,4 +1,5 @@
import {
+ BBReferenceFieldSubType,
CalculationType,
canGroupBy,
FeatureFlag,
@@ -7,6 +8,7 @@ import {
PermissionLevel,
RelationSchemaField,
RenameColumn,
+ RequiredKeys,
Table,
TableSchema,
View,
@@ -325,13 +327,26 @@ export async function enrichSchema(
const viewFieldSchema = viewFields[relTableFieldName]
const isVisible = !!viewFieldSchema?.visible
const isReadonly = !!viewFieldSchema?.readonly
- result[relTableFieldName] = {
- ...relTableField,
- ...viewFieldSchema,
- name: relTableField.name,
+ const enrichedFieldSchema: RequiredKeys = {
visible: isVisible,
readonly: isReadonly,
+ order: viewFieldSchema?.order,
+ width: viewFieldSchema?.width,
+
+ icon: relTableField.icon,
+ type: relTableField.type,
+ subtype: relTableField.subtype,
}
+ if (
+ !enrichedFieldSchema.icon &&
+ relTableField.type === FieldType.BB_REFERENCE &&
+ relTableField.subtype === BBReferenceFieldSubType.USER &&
+ !helpers.schema.isDeprecatedSingleUserColumn(relTableField)
+ ) {
+ // Forcing the icon, otherwise we would need to pass the constraints to show the proper icon
+ enrichedFieldSchema.icon = "UserGroup"
+ }
+ result[relTableFieldName] = enrichedFieldSchema
}
return result
}
diff --git a/packages/server/src/sdk/app/views/tests/views.spec.ts b/packages/server/src/sdk/app/views/tests/views.spec.ts
index 1d7360c5eb..948ffbf096 100644
--- a/packages/server/src/sdk/app/views/tests/views.spec.ts
+++ b/packages/server/src/sdk/app/views/tests/views.spec.ts
@@ -355,13 +355,11 @@ describe("table sdk", () => {
visible: true,
columns: {
title: {
- name: "title",
type: "string",
visible: true,
readonly: true,
},
age: {
- name: "age",
type: "number",
visible: false,
readonly: false,
diff --git a/packages/server/src/utilities/security.ts b/packages/server/src/utilities/security.ts
index 4f93c33ee4..7cfbeb197c 100644
--- a/packages/server/src/utilities/security.ts
+++ b/packages/server/src/utilities/security.ts
@@ -1,5 +1,5 @@
-import { permissions, roles } from "@budibase/backend-core"
-import { DocumentType, VirtualDocumentType } from "../db/utils"
+import { DocumentType, permissions, roles } from "@budibase/backend-core"
+import { VirtualDocumentType } from "../db/utils"
import { getDocumentType, getVirtualDocumentType } from "@budibase/types"
export const CURRENTLY_SUPPORTED_LEVELS: string[] = [
@@ -19,6 +19,7 @@ export function getPermissionType(resourceId: string) {
switch (docType) {
case DocumentType.TABLE:
case DocumentType.ROW:
+ case DocumentType.DATASOURCE_PLUS:
return permissions.PermissionType.TABLE
case DocumentType.AUTOMATION:
return permissions.PermissionType.AUTOMATION
diff --git a/packages/shared-core/src/table.ts b/packages/shared-core/src/table.ts
index 57f6854604..677b1e2357 100644
--- a/packages/shared-core/src/table.ts
+++ b/packages/shared-core/src/table.ts
@@ -67,7 +67,7 @@ const allowDefaultColumnByType: Record = {
[FieldType.SIGNATURE_SINGLE]: false,
[FieldType.LINK]: false,
[FieldType.BB_REFERENCE]: false,
- [FieldType.BB_REFERENCE_SINGLE]: false,
+ [FieldType.BB_REFERENCE_SINGLE]: true,
}
export function canBeDisplayColumn(type: FieldType): boolean {
diff --git a/packages/shared-core/src/utils.ts b/packages/shared-core/src/utils.ts
index 4847e911e9..bead5961f0 100644
--- a/packages/shared-core/src/utils.ts
+++ b/packages/shared-core/src/utils.ts
@@ -172,7 +172,7 @@ export const processSearchFilters = (
.sort((a, b) => {
return a.localeCompare(b)
})
- .filter(key => key in filter)
+ .filter(key => filter[key])
if (filterPropertyKeys.length == 1) {
const key = filterPropertyKeys[0],
diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts
index f9d1a4c012..f5bb081fd5 100644
--- a/packages/types/src/documents/app/table/schema.ts
+++ b/packages/types/src/documents/app/table/schema.ts
@@ -126,6 +126,7 @@ export interface BBReferenceSingleFieldMetadata
extends Omit {
type: FieldType.BB_REFERENCE_SINGLE
subtype: Exclude
+ default?: string
}
export interface AttachmentFieldMetadata extends BaseFieldSchema {
diff --git a/packages/types/src/sdk/view.ts b/packages/types/src/sdk/view.ts
index 422207197d..4c555fbaa7 100644
--- a/packages/types/src/sdk/view.ts
+++ b/packages/types/src/sdk/view.ts
@@ -1,4 +1,10 @@
-import { FieldSchema, RelationSchemaField, ViewV2 } from "../documents"
+import {
+ FieldSchema,
+ FieldSubType,
+ FieldType,
+ RelationSchemaField,
+ ViewV2,
+} from "../documents"
export interface ViewV2Enriched extends ViewV2 {
schema?: {
@@ -8,4 +14,7 @@ export interface ViewV2Enriched extends ViewV2 {
}
}
-export type ViewV2ColumnEnriched = RelationSchemaField & FieldSchema
+export interface ViewV2ColumnEnriched extends RelationSchemaField {
+ type: FieldType
+ subtype?: FieldSubType
+}