diff --git a/packages/backend-core/src/features/index.ts b/packages/backend-core/src/features/index.ts index e0bd29b1bc..5fa73ebabe 100644 --- a/packages/backend-core/src/features/index.ts +++ b/packages/backend-core/src/features/index.ts @@ -1,7 +1,7 @@ import env from "../environment" import * as context from "../context" import { PostHog, PostHogOptions } from "posthog-node" -import { IdentityType, UserCtx } from "@budibase/types" +import { FeatureFlag, IdentityType, UserCtx } from "@budibase/types" import tracer from "dd-trace" let posthog: PostHog | undefined @@ -267,4 +267,5 @@ export class FlagSet, T extends { [key: string]: V }> { // default values set correctly and their types flow through the system. export const flags = new FlagSet({ DEFAULT_VALUES: Flag.boolean(false), + [FeatureFlag.ENRICHED_RELATIONSHIPS]: Flag.boolean(false), }) diff --git a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte index 525421f996..b927a31d0b 100644 --- a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte @@ -18,6 +18,8 @@ import GridEditColumnModal from "components/backend/DataTable/modals/grid/GridEditColumnModal.svelte" import GridUsersTableButton from "components/backend/DataTable/modals/grid/GridUsersTableButton.svelte" import { DB_TYPE_EXTERNAL } from "constants/backend" + import { isEnabled } from "helpers/featureFlags" + import { FeatureFlag } from "@budibase/types" const userSchemaOverrides = { firstName: { displayName: "First name", disabled: true }, @@ -66,6 +68,7 @@ canDeleteRows={!isUsersTable} canEditRows={!isUsersTable || !$appStore.features.disableUserMetadata} canEditColumns={!isUsersTable || !$appStore.features.disableUserMetadata} + canSetRelationshipSchemas={isEnabled(FeatureFlag.ENRICHED_RELATIONSHIPS)} schemaOverrides={isUsersTable ? userSchemaOverrides : null} showAvatars={false} on:updatedatasource={handleGridTableUpdate} diff --git a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte index 646b764a2c..b56c5f6568 100644 --- a/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ViewV2DataTable.svelte @@ -6,6 +6,8 @@ import GridCreateEditRowModal from "components/backend/DataTable/modals/grid/GridCreateEditRowModal.svelte" import GridFilterButton from "components/backend/DataTable/buttons/grid/GridFilterButton.svelte" import GridManageAccessButton from "components/backend/DataTable/buttons/grid/GridManageAccessButton.svelte" + import { isEnabled } from "helpers/featureFlags" + import { FeatureFlag } from "@budibase/types" $: id = $viewsV2.selected?.id $: datasource = { @@ -29,6 +31,7 @@ on:updatedatasource={handleGridViewUpdate} isCloud={$admin.cloud} allowViewReadonlyColumns={$licensing.isViewReadonlyColumnsEnabled} + canSetRelationshipSchemas={isEnabled(FeatureFlag.ENRICHED_RELATIONSHIPS)} > diff --git a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte index cb2ffb1c28..b8f2ac4456 100644 --- a/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte +++ b/packages/frontend-core/src/components/grid/controls/ColumnsSettingContent.svelte @@ -12,7 +12,9 @@ export let columns export let fromRelationshipField - const { datasource, dispatch, cache } = getContext("grid") + const { datasource, dispatch, cache, config } = getContext("grid") + + $: canSetRelationshipSchemas = $config.canSetRelationshipSchemas let relationshipPanelAnchor let relationshipFieldName @@ -30,8 +32,6 @@ {} ) - $: allowRelationshipSchemas = true // TODO - $: displayColumns = columns.map(c => { const isRequired = c.primaryDisplay || helpers.schema.isRequired(c.schema.constraints) @@ -196,7 +196,7 @@ value={columnToPermissionOptions(column)} options={column.options} /> - {#if allowRelationshipSchemas && column.schema.type === FieldType.LINK && columnToPermissionOptions(column) !== FieldPermissions.HIDDEN} + {#if canSetRelationshipSchemas && column.schema.type === FieldType.LINK && columnToPermissionOptions(column) !== FieldPermissions.HIDDEN}
{ @@ -214,7 +214,7 @@
-{#if allowRelationshipSchemas} +{#if canSetRelationshipSchemas} (relationshipFieldName = null)} open={relationshipFieldName} diff --git a/packages/frontend-core/src/components/grid/layout/Grid.svelte b/packages/frontend-core/src/components/grid/layout/Grid.svelte index 04a11da812..f24ff0ae10 100644 --- a/packages/frontend-core/src/components/grid/layout/Grid.svelte +++ b/packages/frontend-core/src/components/grid/layout/Grid.svelte @@ -43,6 +43,7 @@ export let canDeleteRows = true export let canEditColumns = true export let canSaveSchema = true + export let canSetRelationshipSchemas = false export let stripeRows = false export let quiet = false export let collaboration = true @@ -99,6 +100,7 @@ canDeleteRows, canEditColumns, canSaveSchema, + canSetRelationshipSchemas, stripeRows, quiet, collaboration, diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index ca9ef134cb..ca61918cad 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -40,6 +40,7 @@ import { TableSchema, JsonFieldSubType, RowExportFormat, + FeatureFlag, } from "@budibase/types" import { generator, mocks } from "@budibase/backend-core/tests" import _, { merge } from "lodash" @@ -95,7 +96,12 @@ describe.each([ let envCleanup: (() => void) | undefined beforeAll(async () => { - await withCoreEnv({ SQS_SEARCH_ENABLE: "true" }, () => config.init()) + await withCoreEnv( + { + SQS_SEARCH_ENABLE: "true", + }, + () => config.init() + ) if (isSqs) { envCleanup = setCoreEnv({ SQS_SEARCH_ENABLE: "true", @@ -2436,7 +2442,13 @@ describe.each([ let auxData: Row[] = [] + let flagCleanup: (() => void) | undefined + beforeAll(async () => { + flagCleanup = setCoreEnv({ + TENANT_FEATURE_FLAGS: `*:${FeatureFlag.ENRICHED_RELATIONSHIPS}`, + }) + const aux2Table = await config.api.table.save(saveTableRequest()) const aux2Data = await config.api.row.save(aux2Table._id!, {}) @@ -2558,7 +2570,11 @@ describe.each([ tableId = table._id! }) - it.each([ + afterAll(() => { + flagCleanup?.() + }) + + const testScenarios: [string, (row: Row) => Promise | Row][] = [ ["get row", (row: Row) => config.api.row.get(tableId, row._id!)], [ "fetch", @@ -2591,7 +2607,9 @@ describe.each([ }, ], ["from original saved row", (row: Row) => row], - ])( + ] + + it.each(testScenarios)( "can retrieve rows with populated relationships (via %s)", async (__, retrieveDelegate) => { const otherRows = _.sampleSize(auxData, 5) @@ -2606,6 +2624,7 @@ describe.each([ }) const retrieved = await retrieveDelegate(row) + expect(retrieved).toEqual( expect.objectContaining({ title: row.title, @@ -2648,6 +2667,67 @@ describe.each([ ) } ) + + it.each(testScenarios)( + "does not enrich relationships when not enabled (via %s)", + async (__, retrieveDelegate) => { + await withCoreEnv( + { + TENANT_FEATURE_FLAGS: ``, + }, + async () => { + const otherRows = _.sampleSize(auxData, 5) + + const row = await config.api.row.save(tableId, { + title: generator.word(), + relWithNoSchema: [otherRows[0]], + relWithEmptySchema: [otherRows[1]], + relWithFullSchema: [otherRows[2]], + relWithHalfSchema: [otherRows[3]], + relWithIllegalSchema: [otherRows[4]], + }) + + const retrieved = await retrieveDelegate(row) + + expect(retrieved).toEqual( + expect.objectContaining({ + title: row.title, + relWithNoSchema: [ + { + _id: otherRows[0]._id, + primaryDisplay: otherRows[0].name, + }, + ], + relWithEmptySchema: [ + { + _id: otherRows[1]._id, + primaryDisplay: otherRows[1].name, + }, + ], + relWithFullSchema: [ + { + _id: otherRows[2]._id, + primaryDisplay: otherRows[2].name, + }, + ], + relWithHalfSchema: [ + { + _id: otherRows[3]._id, + primaryDisplay: otherRows[3].name, + }, + ], + relWithIllegalSchema: [ + { + _id: otherRows[4]._id, + primaryDisplay: otherRows[4].name, + }, + ], + }) + ) + } + ) + } + ) }) describe("Formula fields", () => { diff --git a/packages/server/src/db/linkedRows/index.ts b/packages/server/src/db/linkedRows/index.ts index 2394d00d0e..3881060d68 100644 --- a/packages/server/src/db/linkedRows/index.ts +++ b/packages/server/src/db/linkedRows/index.ts @@ -11,9 +11,10 @@ import { USER_METDATA_PREFIX } from "../utils" import partition from "lodash/partition" import { getGlobalUsersFromMetadata } from "../../utilities/global" import { processFormulas } from "../../utilities/rowProcessor" -import { context } from "@budibase/backend-core" +import { context, features } from "@budibase/backend-core" import { ContextUser, + FeatureFlag, FieldType, LinkDocumentValue, Row, @@ -272,7 +273,9 @@ export async function squashLinksToPrimaryDisplay( const obj: any = { _id: link._id } obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable) - const allowRelationshipSchemas = true // TODO + const allowRelationshipSchemas = await features.flags.isEnabled( + FeatureFlag.ENRICHED_RELATIONSHIPS + ) if (schema.schema && allowRelationshipSchemas) { for (const relField of Object.entries(schema.schema) .filter(([_, field]) => field.visible !== false) diff --git a/packages/types/src/sdk/featureFlag.ts b/packages/types/src/sdk/featureFlag.ts index 257b4ee576..3d96b63c64 100644 --- a/packages/types/src/sdk/featureFlag.ts +++ b/packages/types/src/sdk/featureFlag.ts @@ -1,6 +1,7 @@ export enum FeatureFlag { PER_CREATOR_PER_USER_PRICE = "PER_CREATOR_PER_USER_PRICE", PER_CREATOR_PER_USER_PRICE_ALERT = "PER_CREATOR_PER_USER_PRICE_ALERT", + ENRICHED_RELATIONSHIPS = "ENRICHED_RELATIONSHIPS", } export interface TenantFeatureFlags {