Skip to content

Commit 4745d51

Browse files
fix(feature-flags): hardcode workflow-columns on, fix feature-flags tests
1 parent f6376eb commit 4745d51

6 files changed

Lines changed: 39 additions & 66 deletions

File tree

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/new-column-dropdown/new-column-dropdown.tsx

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ interface NewColumnDropdownProps {
2727
onPickType: (type: ColumnDefinition['type']) => void
2828
onPickWorkflow: () => void
2929
onPickEnrichment: () => void
30-
workflowColumnsEnabled: boolean
3130
}
3231

3332
/**
@@ -41,12 +40,7 @@ export function NewColumnDropdown({
4140
onPickType,
4241
onPickWorkflow,
4342
onPickEnrichment,
44-
workflowColumnsEnabled,
4543
}: NewColumnDropdownProps) {
46-
const visibleColumnTypeOptions = workflowColumnsEnabled
47-
? COLUMN_TYPE_OPTIONS
48-
: COLUMN_TYPE_OPTIONS.filter((o) => o.type !== 'workflow')
49-
5044
const menu = (
5145
<DropdownMenu>
5246
<DropdownMenuTrigger asChild>
@@ -68,16 +62,14 @@ export function NewColumnDropdown({
6862
)}
6963
</DropdownMenuTrigger>
7064
<DropdownMenuContent align='start' side='bottom' sideOffset={4}>
71-
{workflowColumnsEnabled && (
72-
<>
73-
<DropdownMenuItem onSelect={onPickEnrichment}>
74-
<Sparkles className='size-[14px] text-[var(--text-icon)]' />
75-
Enrichments
76-
</DropdownMenuItem>
77-
<DropdownMenuSeparator />
78-
</>
79-
)}
80-
{visibleColumnTypeOptions.map((option) => {
65+
<>
66+
<DropdownMenuItem onSelect={onPickEnrichment}>
67+
<Sparkles className='size-[14px] text-[var(--text-icon)]' />
68+
Enrichments
69+
</DropdownMenuItem>
70+
<DropdownMenuSeparator />
71+
</>
72+
{COLUMN_TYPE_OPTIONS.map((option) => {
8173
const Icon = option.icon
8274
const onSelect =
8375
option.type === 'workflow'

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,6 @@ interface TableGridProps {
230230
pushTableRenameUndoSinkRef: React.MutableRefObject<
231231
((previousName: string, newName: string) => void) | null
232232
>
233-
workflowColumnsEnabled: boolean
234233
}
235234

236235
/** Serialize a cell value to its tab-separated clipboard representation. */
@@ -301,7 +300,6 @@ export function TableGrid({
301300
afterDeleteAllSinkRef,
302301
confirmDeleteColumnsSinkRef,
303302
pushTableRenameUndoSinkRef,
304-
workflowColumnsEnabled,
305303
}: TableGridProps) {
306304
const params = useParams()
307305
const workspaceId = propWorkspaceId || (params.workspaceId as string)
@@ -3740,7 +3738,6 @@ export function TableGrid({
37403738
onPickType={handleAddColumnOfType}
37413739
onPickWorkflow={handleAddWorkflowColumn}
37423740
onPickEnrichment={onOpenEnrichments}
3743-
workflowColumnsEnabled={workflowColumnsEnabled}
37443741
/>
37453742
)}
37463743
</tr>
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
import type { Metadata } from 'next'
2-
import { headers } from 'next/headers'
3-
import { getSession } from '@/lib/auth'
4-
import { isFeatureEnabled } from '@/lib/core/config/feature-flags'
52
import { Table } from './table'
63

74
export const metadata: Metadata = {
85
title: 'Table',
96
}
107

11-
export default async function TablePage() {
12-
const session = await getSession(await headers())
13-
const workflowColumnsEnabled = await isFeatureEnabled('workflow-columns', {
14-
userId: session?.user?.id,
15-
})
16-
return <Table workflowColumnsEnabled={workflowColumnsEnabled} />
8+
export default function TablePage() {
9+
return <Table />
1710
}

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ interface TableProps {
6464
/** Identifiers — only set in embedded mode. Page mode reads from `useParams()`. */
6565
workspaceId?: string
6666
tableId?: string
67-
/** Resolved server-side from the workflow-columns feature flag. */
68-
workflowColumnsEnabled?: boolean
6967
}
7068

7169
/**
@@ -118,7 +116,6 @@ export function Table({
118116
embedded,
119117
workspaceId: propWorkspaceId,
120118
tableId: propTableId,
121-
workflowColumnsEnabled = false,
122119
}: TableProps = {}) {
123120
const params = useParams()
124121
const router = useRouter()
@@ -561,7 +558,6 @@ export function Table({
561558
onPickType={handleAddColumnOfType}
562559
onPickWorkflow={handleAddWorkflowColumn}
563560
onPickEnrichment={onOpenEnrichments}
564-
workflowColumnsEnabled={workflowColumnsEnabled}
565561
/>
566562
) : null
567563

@@ -695,7 +691,6 @@ export function Table({
695691
afterDeleteAllSinkRef={afterDeleteAllSinkRef}
696692
confirmDeleteColumnsSinkRef={confirmDeleteColumnsSinkRef}
697693
pushTableRenameUndoSinkRef={pushTableRenameUndoSinkRef}
698-
workflowColumnsEnabled={workflowColumnsEnabled}
699694
/>
700695
{userPermissions.canEdit && (
701696
<TableActionBar

apps/sim/lib/core/config/feature-flags.test.ts

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
* @vitest-environment node
33
*/
44
import { beforeEach, describe, expect, it, vi } from 'vitest'
5-
import type {
6-
FeatureFlagContext,
7-
FeatureFlagName,
8-
FeatureFlagsConfig,
9-
} from '@/lib/core/config/feature-flags'
5+
import type { FeatureFlagContext, FeatureFlagName } from '@/lib/core/config/feature-flags'
106

117
const { mockFetch, mockIsPlatformAdmin, envRef, flagRef } = vi.hoisted(() => ({
128
mockFetch: vi.fn(),
@@ -23,6 +19,7 @@ vi.mock('@/lib/core/config/appconfig', () => ({
2319
}))
2420

2521
vi.mock('@/lib/core/config/env', () => ({
22+
isTruthy: (v: unknown) => Boolean(v),
2623
get env() {
2724
return envRef
2825
},
@@ -60,21 +57,22 @@ describe('getFeatureFlags', () => {
6057
flagRef.isAppConfigEnabled = false
6158
})
6259

63-
it('derives flags from fallback secrets (empty registry → empty) when AppConfig is disabled, without fetching', async () => {
64-
expect(await getFeatureFlags()).toEqual<FeatureFlagsConfig>({ flags: {} })
60+
it('derives flags from fallback secrets when AppConfig is disabled, without fetching', async () => {
61+
const flags = await getFeatureFlags()
62+
// All registered flags should be present, disabled (env vars unset in test env)
63+
expect(flags['tables-fractional-ordering']).toEqual({ enabled: false })
64+
expect(flags['mothership-beta']).toEqual({ enabled: false })
6565
expect(mockFetch).not.toHaveBeenCalled()
6666
})
6767

6868
it('reads the feature-flags profile and normalizes the payload when enabled', async () => {
6969
withAppConfig({
70-
flags: {
71-
a: { enabled: true },
72-
b: { orgIds: ['Org_1', ' org_1 ', '', 'org_2'], userIds: 'nope' },
73-
c: 'not-an-object',
74-
},
70+
a: { enabled: true },
71+
b: { orgIds: ['Org_1', ' org_1 ', '', 'org_2'], userIds: 'nope' },
72+
c: 'not-an-object',
7573
})
7674

77-
const { flags } = await getFeatureFlags()
75+
const flags = await getFeatureFlags()
7876
expect(flags.a).toEqual({ enabled: true })
7977
expect(flags.b).toEqual({ orgIds: ['Org_1', 'org_1', 'org_2'] })
8078
expect(flags.c).toBeUndefined()
@@ -87,14 +85,16 @@ describe('getFeatureFlags', () => {
8785
it('falls back to the secret-derived document when the fetch yields null', async () => {
8886
flagRef.isAppConfigEnabled = true
8987
mockFetch.mockResolvedValue(null)
90-
expect(await getFeatureFlags()).toEqual<FeatureFlagsConfig>({ flags: {} })
88+
const flags = await getFeatureFlags()
89+
expect(flags['tables-fractional-ordering']).toEqual({ enabled: false })
90+
expect(flags['mothership-beta']).toEqual({ enabled: false })
9191
})
9292

9393
it('degrades gracefully on a malformed document', async () => {
94-
withAppConfig({ flags: 'not-an-object' })
95-
expect(await getFeatureFlags()).toEqual<FeatureFlagsConfig>({ flags: {} })
94+
withAppConfig('not-an-object')
95+
expect(await getFeatureFlags()).toMatchObject({})
9696
withAppConfig(null)
97-
expect(await getFeatureFlags()).toEqual<FeatureFlagsConfig>({ flags: {} })
97+
expect(await getFeatureFlags()).toMatchObject({})
9898
})
9999
})
100100

@@ -105,31 +105,31 @@ describe('isFeatureEnabled', () => {
105105
})
106106

107107
it('returns false for an unknown flag', async () => {
108-
withAppConfig({ flags: {} })
108+
withAppConfig({})
109109
expect(await enabled('missing', { userId: 'u1' })).toBe(false)
110110
})
111111

112112
it('matches the global enabled clause', async () => {
113-
withAppConfig({ flags: { f: { enabled: true } } })
113+
withAppConfig({ f: { enabled: true } })
114114
expect(await enabled('f')).toBe(true)
115115
})
116116

117117
it('matches the userId allowlist', async () => {
118-
withAppConfig({ flags: { f: { userIds: ['u1'] } } })
118+
withAppConfig({ f: { userIds: ['u1'] } })
119119
expect(await enabled('f', { userId: 'u1' })).toBe(true)
120120
expect(await enabled('f', { userId: 'u2' })).toBe(false)
121121
expect(await enabled('f', {})).toBe(false)
122122
})
123123

124124
it('matches the orgId allowlist', async () => {
125-
withAppConfig({ flags: { f: { orgIds: ['o1'] } } })
125+
withAppConfig({ f: { orgIds: ['o1'] } })
126126
expect(await enabled('f', { orgId: 'o1' })).toBe(true)
127127
expect(await enabled('f', { orgId: 'o2' })).toBe(false)
128128
})
129129

130130
describe('admin clause (lazy resolution)', () => {
131-
it('resolves admin from userId when admins is the deciding clause', async () => {
132-
withAppConfig({ flags: { f: { admins: true } } })
131+
it('resolves admin from userId when adminEnabled is the deciding clause', async () => {
132+
withAppConfig({ f: { adminEnabled: true } })
133133
mockIsPlatformAdmin.mockResolvedValue(true)
134134
expect(await enabled('f', { userId: 'u1' })).toBe(true)
135135
expect(mockIsPlatformAdmin).toHaveBeenCalledWith('u1')
@@ -139,28 +139,28 @@ describe('isFeatureEnabled', () => {
139139
})
140140

141141
it('uses the isAdmin override without querying', async () => {
142-
withAppConfig({ flags: { f: { admins: true } } })
142+
withAppConfig({ f: { adminEnabled: true } })
143143
expect(await enabled('f', { userId: 'u1', isAdmin: true })).toBe(true)
144144
expect(mockIsPlatformAdmin).not.toHaveBeenCalled()
145145
})
146146

147147
it('resolves to false without querying when userId is absent', async () => {
148-
withAppConfig({ flags: { f: { admins: true } } })
148+
withAppConfig({ f: { adminEnabled: true } })
149149
expect(await enabled('f', { orgId: 'o1' })).toBe(false)
150150
expect(mockIsPlatformAdmin).not.toHaveBeenCalled()
151151
})
152152

153153
it('does not query when an earlier clause already matched', async () => {
154-
withAppConfig({ flags: { f: { enabled: true, admins: true } } })
154+
withAppConfig({ f: { enabled: true, adminEnabled: true } })
155155
expect(await enabled('f', { userId: 'u1' })).toBe(true)
156156

157-
withAppConfig({ flags: { g: { userIds: ['u1'], admins: true } } })
157+
withAppConfig({ g: { userIds: ['u1'], adminEnabled: true } })
158158
expect(await enabled('g', { userId: 'u1' })).toBe(true)
159159
expect(mockIsPlatformAdmin).not.toHaveBeenCalled()
160160
})
161161

162-
it('does not query when the rule has no admins clause', async () => {
163-
withAppConfig({ flags: { f: { userIds: ['u2'] } } })
162+
it('does not query when the rule has no adminEnabled clause', async () => {
163+
withAppConfig({ f: { userIds: ['u2'] } })
164164
expect(await enabled('f', { userId: 'u1' })).toBe(false)
165165
expect(mockIsPlatformAdmin).not.toHaveBeenCalled()
166166
})

apps/sim/lib/core/config/feature-flags.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,6 @@ const FEATURE_FLAGS = {
7171
'Mothership beta plan/changelog artifact surfaces in the copilot VFS and doc compiler',
7272
fallback: 'MOTHERSHIP_BETA_FEATURES',
7373
},
74-
'workflow-columns': {
75-
description: 'Workflow column type and enrichments in the table new-column dropdown',
76-
fallback: 'NEXT_PUBLIC_WORKFLOW_COLUMNS_ENABLED',
77-
},
7874
} satisfies Record<string, FeatureFlagDefinition>
7975

8076
/**

0 commit comments

Comments
 (0)