chore(studio): remove tableEditorApiAccessToggle flag (#45081)

Cleans up the `tableEditorApiAccessToggle` PostHog flag now that the
gated UI is shipping to everyone. Follow-up to #45034 — the new
project-creation checkbox makes the management UI a prerequisite, so no
reason to keep it behind a flag.

**Removed:**
- `useDataApiGrantTogglesEnabled` hook
- Old schemas-only multi-selector branch in the Data API settings page
(the rich per-table / per-function toggles + default-privileges switch
become the only UI)
- Flag gate around the `<ApiAccessToggle>` section in the table editor
side panel
- Flag gates around `updateTableApiAccess` calls in the save pipeline
(create / duplicate / update)
- `tableEditorApiAccessToggleEnabled` telemetry property + stale JSDoc /
docs references

**Changed:**
- `createTableApiAccessHandlerParams` no longer takes an `enabled` param
— it was always `true` after removal

## To test

- Integrations → Data API settings page: exposed tables, exposed
functions, default-privileges toggle all render and save correctly
- Table editor: creating, duplicating, and editing a table all run the
expected Data API privilege updates
- Project creation flow still works end-to-end (unchanged, but the
submit telemetry no longer includes `tableEditorApiAccessToggleEnabled`)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Improvements**
* API access configuration is now always available in the table editor
and PostgreSQL settings, removing previous conditional gating.
* Simplified the "Automatically expose new tables and functions"
interface by consolidating UI branches.

* **Documentation**
* Updated telemetry guidance and examples with current feature-flag
references.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
This commit is contained in:
Alaister Young
2026-04-22 21:37:48 +08:00
committed by GitHub
parent 3838e32d33
commit 75e08577c1
8 changed files with 174 additions and 350 deletions
@@ -1,5 +1,5 @@
---
applyTo: "apps/studio/**,packages/common/telemetry*"
applyTo: 'apps/studio/**,packages/common/telemetry*'
---
# Studio Telemetry Review Rules
@@ -11,8 +11,8 @@ All comments are **advisory** — suggest, do not request changes.
Use judgment — not every PR needs telemetry. But **always flag** when:
1. **Changes to `packages/common/telemetry-constants.ts`** — validate event naming, property conventions, and JSDoc accuracy.
2. **PostHog feature flags without measurement.** If a PR uses `usePHFlag` or PostHog-backed hooks like `useDataApiGrantTogglesEnabled` to gate behavior, the flag state should be captured in a telemetry event so the rollout can be measured. Flag if the flag value isn't included in a relevant `track()` call. (Note: `useFlag` from `common` reads ConfigCat flags, not PostHog — different system, different guidance.)
3. **Feature-flagged rollouts without outcome tracking.** If a flag gates new behavior, there should be telemetry on both the flag state *and* how users respond to the new behavior (e.g., toggle clicks, opt-in actions).
2. **PostHog feature flags without measurement.** If a PR uses `usePHFlag` or PostHog-backed hooks like `useDataApiRevokeOnCreateDefaultEnabled` to gate behavior, the flag state should be captured in a telemetry event so the rollout can be measured. Flag if the flag value isn't included in a relevant `track()` call. (Note: `useFlag` from `common` reads ConfigCat flags, not PostHog — different system, different guidance.)
3. **Feature-flagged rollouts without outcome tracking.** If a flag gates new behavior, there should be telemetry on both the flag state _and_ how users respond to the new behavior (e.g., toggle clicks, opt-in actions).
4. **Growth-oriented components adding user interactions without tracking** — onboarding flows, setup wizards, upgrade CTAs, A/B experiment variants.
When tracking is missing, comment: _"This adds a user interaction (or feature flag) that may benefit from tracking."_ Then propose an event name and `useTrack()` call.
@@ -52,6 +52,7 @@ Flag: unapproved verbs (`saved`, `viewed`, `pressed`), wrong order (`click_produ
```typescript
import { useTrack } from 'lib/telemetry/track'
const track = useTrack()
track('product_card_clicked', { productType: 'database', planTier: 'pro' })
```
@@ -3,7 +3,6 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { useParams } from 'common'
import { Lock } from 'lucide-react'
import Link from 'next/link'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useForm } from 'react-hook-form'
import { toast } from 'sonner'
@@ -43,17 +42,14 @@ import { ExposedTableSelector } from '@/components/interfaces/Settings/API/Expos
import { FormActions } from '@/components/ui/Forms/FormActions'
import { useProjectPostgrestConfigQuery } from '@/data/config/project-postgrest-config-query'
import { useProjectPostgrestConfigUpdateMutation } from '@/data/config/project-postgrest-config-update-mutation'
import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
import { useSchemasQuery } from '@/data/database/schemas-query'
import { defaultPrivilegesQueryOptions } from '@/data/privileges/default-privileges-query'
import { privilegeKeys } from '@/data/privileges/keys'
import { useUpdateDefaultPrivilegesMutation } from '@/data/privileges/update-default-privileges-mutation'
import { useUpdateExposedEntitiesMutation } from '@/data/privileges/update-exposed-entities-mutation'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useDataApiGrantTogglesEnabled } from '@/hooks/misc/useDataApiGrantTogglesEnabled'
import useLatest from '@/hooks/misc/useLatest'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { INTERNAL_SCHEMAS } from '@/hooks/useProtectedSchemas'
import { IS_PLATFORM } from '@/lib/constants'
import { noop } from '@/lib/void'
import type { ResponseError } from '@/types'
@@ -84,7 +80,6 @@ export const PostgrestConfig = () => {
const { ref: projectRef } = useParams()
const { data: project } = useSelectedProjectQuery()
const queryClient = useQueryClient()
const isApiGrantTogglesEnabled = useDataApiGrantTogglesEnabled()
const [showModal, setShowModal] = useState(false)
@@ -94,10 +89,6 @@ export const PostgrestConfig = () => {
isPending: isLoadingConfig,
isSuccess: isSuccessConfig,
} = useProjectPostgrestConfigQuery({ projectRef })
const { data: extensions } = useDatabaseExtensionsQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
const {
data: allSchemas = [],
isPending: isLoadingSchemas,
@@ -125,24 +116,6 @@ export const PostgrestConfig = () => {
const isLoading = isLoadingConfig || isLoadingSchemas || isLoadingDefaultPrivileges
const schemas = useMemo(
() =>
allSchemas
.filter((x) => {
if (x.name === 'graphql_public') return true
return !INTERNAL_SCHEMAS.includes(x.name)
})
.map((x) => {
return {
id: x.id,
value: x.name,
name: x.name,
disabled: false,
}
}) ?? [],
[allSchemas]
)
const { mutateAsync: updatePostgrestConfig } = useProjectPostgrestConfigUpdateMutation({
onError: noop,
})
@@ -159,9 +132,6 @@ export const PostgrestConfig = () => {
useAsyncCheckPermissions(PermissionAction.UPDATE, 'custom_config_postgrest')
const canUpdatePostgrestConfig = IS_PLATFORM && canUpdatePostgrestConfigPermission
const isGraphqlExtensionEnabled =
(extensions ?? []).find((ext) => ext.name === 'pg_graphql')?.installed_version !== null
const defaultValues = useMemo(() => {
return {
dbSchema: configDbSchemas,
@@ -198,23 +168,21 @@ export const PostgrestConfig = () => {
try {
let dbSchema = values.dbSchema.join(',')
if (isApiGrantTogglesEnabled) {
await updateExposedEntities({
await updateExposedEntities({
projectRef,
connectionString: project?.connectionString,
tableIdsToAdd: values.tableIdsToAdd,
tableIdsToRemove: values.tableIdsToRemove,
functionNamesToAdd: values.functionNamesToAdd,
functionNamesToRemove: values.functionNamesToRemove,
})
if (values.defaultPrivilegesGranted !== defaultPrivilegesGranted) {
await updateDefaultPrivileges({
projectRef,
connectionString: project?.connectionString,
tableIdsToAdd: values.tableIdsToAdd,
tableIdsToRemove: values.tableIdsToRemove,
functionNamesToAdd: values.functionNamesToAdd,
functionNamesToRemove: values.functionNamesToRemove,
granted: values.defaultPrivilegesGranted,
})
if (values.defaultPrivilegesGranted !== defaultPrivilegesGranted) {
await updateDefaultPrivileges({
projectRef,
connectionString: project?.connectionString,
granted: values.defaultPrivilegesGranted,
})
}
}
await updatePostgrestConfig(
@@ -308,238 +276,150 @@ export const PostgrestConfig = () => {
</CardContent>
) : (
<>
{isApiGrantTogglesEnabled ? (
<CardContent className="space-y-6">
<FormItemLayout
isReactForm={false}
layout="flex-row-reverse"
label="Exposed schemas"
description="Select schemas to include in the Data API. Schemas must be included before tables can be exposed."
>
<ExposedSchemaSelector
selectedSchemas={watchedDbSchema}
disabled={!canUpdatePostgrestConfig}
onToggleSchema={(schema) => {
const current = form.getValues('dbSchema')
if (current.includes(schema)) {
form.setValue(
'dbSchema',
current.filter((x) => x !== schema),
{ shouldDirty: true }
)
} else {
form.setValue('dbSchema', [...current, schema], {
shouldDirty: true,
})
}
}}
/>
</FormItemLayout>
<CardContent className="space-y-6">
<FormItemLayout
isReactForm={false}
layout="flex-row-reverse"
label="Exposed schemas"
description="Select schemas to include in the Data API. Schemas must be included before tables can be exposed."
>
<ExposedSchemaSelector
selectedSchemas={watchedDbSchema}
disabled={!canUpdatePostgrestConfig}
onToggleSchema={(schema) => {
const current = form.getValues('dbSchema')
if (current.includes(schema)) {
form.setValue(
'dbSchema',
current.filter((x) => x !== schema),
{ shouldDirty: true }
)
} else {
form.setValue('dbSchema', [...current, schema], {
shouldDirty: true,
})
}
}}
/>
</FormItemLayout>
<FormItemLayout
isReactForm={false}
layout="flex-row-reverse"
label="Exposed tables"
description="Toggle Data API access for individual tables."
>
<ExposedTableSelector
selectedSchemas={watchedDbSchema}
pendingAddTableIds={watchedTableIdsToAdd}
pendingRemoveTableIds={watchedTableIdsToRemove}
onTogglePendingAdd={(tableId) => {
const current = form.getValues('tableIdsToAdd')
if (current.includes(tableId)) {
form.setValue(
'tableIdsToAdd',
current.filter((x) => x !== tableId),
{ shouldDirty: true }
)
} else {
form.setValue('tableIdsToAdd', [...current, tableId], {
shouldDirty: true,
})
}
}}
onTogglePendingRemove={(tableId) => {
const current = form.getValues('tableIdsToRemove')
if (current.includes(tableId)) {
form.setValue(
'tableIdsToRemove',
current.filter((x) => x !== tableId),
{ shouldDirty: true }
)
} else {
form.setValue('tableIdsToRemove', [...current, tableId], {
shouldDirty: true,
})
}
}}
/>
</FormItemLayout>
<FormItemLayout
isReactForm={false}
layout="flex-row-reverse"
label="Exposed tables"
description="Toggle Data API access for individual tables."
>
<ExposedTableSelector
selectedSchemas={watchedDbSchema}
pendingAddTableIds={watchedTableIdsToAdd}
pendingRemoveTableIds={watchedTableIdsToRemove}
onTogglePendingAdd={(tableId) => {
const current = form.getValues('tableIdsToAdd')
if (current.includes(tableId)) {
form.setValue(
'tableIdsToAdd',
current.filter((x) => x !== tableId),
{ shouldDirty: true }
)
} else {
form.setValue('tableIdsToAdd', [...current, tableId], {
shouldDirty: true,
})
}
}}
onTogglePendingRemove={(tableId) => {
const current = form.getValues('tableIdsToRemove')
if (current.includes(tableId)) {
form.setValue(
'tableIdsToRemove',
current.filter((x) => x !== tableId),
{ shouldDirty: true }
)
} else {
form.setValue('tableIdsToRemove', [...current, tableId], {
shouldDirty: true,
})
}
}}
/>
</FormItemLayout>
<FormItemLayout
isReactForm={false}
layout="flex-row-reverse"
label="Exposed functions"
description="Toggle Data API access for individual functions."
>
<ExposedFunctionSelector
selectedSchemas={watchedDbSchema}
pendingAddFunctionNames={watchedFunctionNamesToAdd}
pendingRemoveFunctionNames={watchedFunctionNamesToRemove}
onTogglePendingAdd={(functionName) => {
const current = form.getValues('functionNamesToAdd')
if (current.includes(functionName)) {
form.setValue(
'functionNamesToAdd',
current.filter((x) => x !== functionName),
{ shouldDirty: true }
)
} else {
form.setValue('functionNamesToAdd', [...current, functionName], {
shouldDirty: true,
})
}
}}
onTogglePendingRemove={(functionName) => {
const current = form.getValues('functionNamesToRemove')
if (current.includes(functionName)) {
form.setValue(
'functionNamesToRemove',
current.filter((x) => x !== functionName),
{ shouldDirty: true }
)
} else {
form.setValue('functionNamesToRemove', [...current, functionName], {
shouldDirty: true,
})
}
}}
/>
</FormItemLayout>
<FormItemLayout
isReactForm={false}
layout="flex-row-reverse"
label="Exposed functions"
description="Toggle Data API access for individual functions."
>
<ExposedFunctionSelector
selectedSchemas={watchedDbSchema}
pendingAddFunctionNames={watchedFunctionNamesToAdd}
pendingRemoveFunctionNames={watchedFunctionNamesToRemove}
onTogglePendingAdd={(functionName) => {
const current = form.getValues('functionNamesToAdd')
if (current.includes(functionName)) {
form.setValue(
'functionNamesToAdd',
current.filter((x) => x !== functionName),
{ shouldDirty: true }
)
} else {
form.setValue('functionNamesToAdd', [...current, functionName], {
shouldDirty: true,
})
}
}}
onTogglePendingRemove={(functionName) => {
const current = form.getValues('functionNamesToRemove')
if (current.includes(functionName)) {
form.setValue(
'functionNamesToRemove',
current.filter((x) => x !== functionName),
{ shouldDirty: true }
)
} else {
form.setValue('functionNamesToRemove', [...current, functionName], {
shouldDirty: true,
})
}
}}
/>
</FormItemLayout>
{watchedDbSchema.includes('public') && (
<FormField_Shadcn_
control={form.control}
name="defaultPrivilegesGranted"
render={({ field }) => (
<FormItem_Shadcn_>
<FormItemLayout
layout="flex-row-reverse"
label="Automatically expose new tables and functions"
description="Grants privileges to Data API roles by default, exposing new tables and functions. We recommend disabling this to control access manually."
>
<FormControl_Shadcn_>
<div>
<Switch
size="large"
disabled={!canUpdatePostgrestConfig}
checked={field.value}
onCheckedChange={field.onChange}
/>
</div>
</FormControl_Shadcn_>
</FormItemLayout>
</FormItem_Shadcn_>
)}
/>
)}
{watchedDbSchema.length === 0 && (
<Admonition
type="warning"
title="No schema is currently selected"
description="Saving with no selected schema or table will disable the Data API."
/>
)}
</CardContent>
) : (
<CardContent>
{watchedDbSchema.includes('public') && (
<FormField_Shadcn_
control={form.control}
name="dbSchema"
name="defaultPrivilegesGranted"
render={({ field }) => (
<FormItem_Shadcn_>
<FormItemLayout
label="Exposed schemas"
description="The schemas to expose in your API. Tables, views and functions in
these schemas will get API endpoints."
layout="flex-row-reverse"
label="Automatically expose new tables and functions"
description="Grants privileges to Data API roles by default, exposing new tables and functions. We recommend disabling this to control access manually."
>
{isLoadingSchemas ? (
<div className="col-span-12 flex flex-col gap-2 lg:col-span-7">
<Skeleton className="w-full h-[38px]" />
</div>
) : (
<MultiSelector
onValuesChange={field.onChange}
values={field.value}
size="small"
disabled={!canUpdatePostgrestConfig}
>
<MultiSelectorTrigger
mode="inline-combobox"
label="Select schemas..."
badgeLimit="wrap"
showIcon={false}
deletableBadge
<FormControl_Shadcn_>
<div>
<Switch
size="large"
disabled={!canUpdatePostgrestConfig}
checked={field.value}
onCheckedChange={field.onChange}
/>
<MultiSelectorContent>
<MultiSelectorList>
{schemas.length <= 0 ? (
<MultiSelectorItem key="empty" value="no">
no
</MultiSelectorItem>
) : (
schemas.map((x) => (
<MultiSelectorItem
key={x.id + '-' + x.name}
value={x.name}
>
{x.name}
</MultiSelectorItem>
))
)}
</MultiSelectorList>
</MultiSelectorContent>
</MultiSelector>
)}
</div>
</FormControl_Shadcn_>
</FormItemLayout>
{!field.value.includes('public') && field.value.length > 0 && (
<Admonition
type="default"
title="The public schema for this project is not exposed"
className="mt-2"
description={
<>
<p className="text-sm">
You will not be able to query tables and views in the{' '}
<code className="text-code-inline">public</code> schema via
supabase-js or HTTP clients.
</p>
{isGraphqlExtensionEnabled && (
<>
<p className="text-sm">
Tables in the{' '}
<code className="text-code-inline">public</code> schema
are still exposed over our GraphQL endpoints.
</p>
<Button asChild type="default" className="mt-2">
<Link href={`/project/${projectRef}/database/extensions`}>
Disable the pg_graphql extension
</Link>
</Button>
</>
)}
</>
}
/>
)}
</FormItem_Shadcn_>
)}
/>
</CardContent>
)}
)}
{watchedDbSchema.length === 0 && (
<Admonition
type="warning"
title="No schema is currently selected"
description="Saving with no selected schema or table will disable the Data API."
/>
)}
</CardContent>
<CardContent>
<FormField_Shadcn_
control={form.control}
@@ -56,7 +56,6 @@ import { tableRowKeys } from '@/data/table-rows/keys'
import { tableKeys } from '@/data/tables/keys'
import { RetrieveTableResult } from '@/data/tables/table-retrieve-query'
import { getTables } from '@/data/tables/tables-query'
import { useDataApiGrantTogglesEnabled } from '@/hooks/misc/useDataApiGrantTogglesEnabled'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { useConfirmOnClose } from '@/hooks/ui/useConfirmOnClose'
@@ -137,16 +136,12 @@ const DUMMY_TABLE_API_ACCESS_PARAMS: TableApiAccessParams = {
}
const createTableApiAccessHandlerParams = ({
enabled,
snap,
selectedTable,
}: {
enabled: boolean
snap: DeepReadonly<TableEditorState>
selectedTable?: PostgresTable
}): TableApiAccessParams | undefined => {
if (!enabled) return undefined
const tableSidePanel = snap.sidePanel?.type === 'table' ? snap.sidePanel : undefined
if (!tableSidePanel) return undefined
@@ -198,7 +193,6 @@ export const SidePanelEditor = ({
const queryClient = useQueryClient()
const { data: project } = useSelectedProjectQuery()
const { data: org } = useSelectedOrganizationQuery()
const isApiGrantTogglesEnabled = useDataApiGrantTogglesEnabled()
const isQueueOperationsEnabled = useIsQueueOperationsEnabled()
const { updateRow, addRow, isEditPending } = useTableRowOperations()
@@ -211,7 +205,6 @@ export const SidePanelEditor = ({
})
const tableApiAccessParams = createTableApiAccessHandlerParams({
enabled: isApiGrantTogglesEnabled,
snap,
selectedTable,
})
@@ -595,7 +588,7 @@ export const SidePanelEditor = ({
let toastId
let saveTableError = false
if (isApiGrantTogglesEnabled && !apiAccessToggleHandler.isSuccess) {
if (!apiAccessToggleHandler.isSuccess) {
if (apiAccessToggleHandler.isPending) {
toast.info(
'Cannot save table yet because Data API settings are still loading. Please try again in a moment.'
@@ -687,13 +680,11 @@ export const SidePanelEditor = ({
async () => {
if (isRealtimeEnabled) await updateTableRealtime(table, true)
if (isApiGrantTogglesEnabled) {
const privilegesToSet = apiAccessToggleHandler.data?.schemaExposed
? apiAccessToggleHandler.data.privileges
: undefined
if (privilegesToSet) {
await updateTableApiAccess(table, privilegesToSet)
}
const privilegesToSet = apiAccessToggleHandler.data?.schemaExposed
? apiAccessToggleHandler.data.privileges
: undefined
if (privilegesToSet) {
await updateTableApiAccess(table, privilegesToSet)
}
}
)
@@ -762,13 +753,11 @@ export const SidePanelEditor = ({
})
if (isRealtimeEnabled) await updateTableRealtime(table, isRealtimeEnabled)
if (isApiGrantTogglesEnabled) {
const privilegesToSet = apiAccessToggleHandler.data?.schemaExposed
? apiAccessToggleHandler.data.privileges
: undefined
if (privilegesToSet) {
await updateTableApiAccess(table, privilegesToSet)
}
const privilegesToSet = apiAccessToggleHandler.data?.schemaExposed
? apiAccessToggleHandler.data.privileges
: undefined
if (privilegesToSet) {
await updateTableApiAccess(table, privilegesToSet)
}
await Promise.all([
@@ -808,13 +797,11 @@ export const SidePanelEditor = ({
}
if (isTableLike(table)) {
await updateTableRealtime(table, isRealtimeEnabled)
if (isApiGrantTogglesEnabled) {
const privilegesToSet = apiAccessToggleHandler.data?.schemaExposed
? apiAccessToggleHandler.data.privileges
: undefined
if (privilegesToSet) {
await updateTableApiAccess(table, privilegesToSet)
}
const privilegesToSet = apiAccessToggleHandler.data?.schemaExposed
? apiAccessToggleHandler.data.privileges
: undefined
if (privilegesToSet) {
await updateTableApiAccess(table, privilegesToSet)
}
}
@@ -32,7 +32,6 @@ import { useForeignKeyConstraintsQuery } from '@/data/database/foreign-key-const
import { useEnumeratedTypesQuery } from '@/data/enumerated-types/enumerated-types-query'
import { useCustomContent } from '@/hooks/custom-content/useCustomContent'
import { useChanged } from '@/hooks/misc/useChanged'
import { useDataApiGrantTogglesEnabled } from '@/hooks/misc/useDataApiGrantTogglesEnabled'
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
import { useQuerySchemaState } from '@/hooks/misc/useSchemaQueryState'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
@@ -79,8 +78,6 @@ export const TableEditor = ({
const { realtimeAll: realtimeEnabled } = useIsFeatureEnabled(['realtime:all'])
const { docsRowLevelSecurityGuidePath } = useCustomContent(['docs:row_level_security_guide_path'])
const isApiGrantTogglesEnabled = useDataApiGrantTogglesEnabled()
const [params, setParams] = useUrlState()
const { data: project } = useSelectedProjectQuery()
const { selectedSchema } = useQuerySchemaState()
@@ -528,22 +525,18 @@ export const TableEditor = ({
</>
)}
{isApiGrantTogglesEnabled && (
<>
<SidePanel.Separator />
<SidePanel.Content className="py-6 space-y-6">
<ApiAccessToggle
projectRef={project?.ref}
schemaName={isNewRecord ? selectedSchema : table?.schema}
tableName={
isNewRecord || isDuplicating ? tableFields.name : tableFields.name || table?.name
}
isNewRecord={isNewRecord || isDuplicating}
handler={apiAccessToggleHandler}
/>
</SidePanel.Content>
</>
)}
<SidePanel.Separator />
<SidePanel.Content className="py-6 space-y-6">
<ApiAccessToggle
projectRef={project?.ref}
schemaName={isNewRecord ? selectedSchema : table?.schema}
tableName={
isNewRecord || isDuplicating ? tableFields.name : tableFields.name || table?.name
}
isNewRecord={isNewRecord || isDuplicating}
handler={apiAccessToggleHandler}
/>
</SidePanel.Content>
</SidePanel>
)
}
@@ -1,22 +0,0 @@
import { usePHFlag } from '../ui/useFlag'
import { IS_TEST_ENV } from '@/lib/constants'
/**
* Determine whether a user has access to Data API grant toggles.
*
* Requires that the ConfigCat flag for Data API badges and the PostHog flag
* for Table Editor API access are both enabled.
*
* In test environments, this returns true to allow E2E testing of the feature
* without requiring the feature flag infrastructure.
*/
export const useDataApiGrantTogglesEnabled = (): boolean => {
const isTableEditorApiAccessEnabled = usePHFlag<boolean>('tableEditorApiAccessToggle')
// In test environment, enable the feature for E2E testing
if (IS_TEST_ENV) {
return true
}
return !!isTableEditorApiAccessEnabled
}
@@ -5,13 +5,10 @@ import { IS_TEST_ENV } from '@/lib/constants'
import { useTrack } from '@/lib/telemetry/track'
/**
* Controls the default state of the "Default privileges for new entities"
* Controls the default state of the "Automatically expose new tables and functions"
* checkbox at project creation. When the flag is on, the checkbox defaults
* to unchecked (i.e. revoke SQL runs). When off/absent, the checkbox defaults
* to checked (current behaviour default grants remain).
*
* Scoped to project-creation only. The existing `tableEditorApiAccessToggle`
* flag continues to gate the integrations Data API settings surface.
*/
export const useDataApiRevokeOnCreateDefaultEnabled = (): boolean => {
const flag = usePHFlag<boolean>('dataApiRevokeOnCreateDefault')
-4
View File
@@ -104,7 +104,6 @@ const Wizard: NextPageWithLayout = () => {
// Read the raw flag for telemetry — coerce-undefined-to-false would record false for
// users whose flags haven't loaded yet. The raw value preserves undefined (omitted from
// PostHog) so we only record true/false when the flag is resolved.
const tableEditorApiAccessToggleFlag = usePHFlag<boolean>('tableEditorApiAccessToggle')
const dataApiRevokeOnCreateDefaultFlag = usePHFlag<boolean>('dataApiRevokeOnCreateDefault')
const isDataApiRevokeOnCreateDefault = useDataApiRevokeOnCreateDefaultEnabled()
@@ -274,9 +273,6 @@ const Wizard: NextPageWithLayout = () => {
dataApiEnabled: form.getValues('dataApi'),
dataApiDefaultPrivilegesGranted: form.getValues('dataApiDefaultPrivileges'),
useOrioleDb: form.getValues('useOrioleDb'),
...(tableEditorApiAccessToggleFlag !== undefined && {
tableEditorApiAccessToggleEnabled: tableEditorApiAccessToggleFlag,
}),
...(dataApiRevokeOnCreateDefaultFlag !== undefined && {
dataApiRevokeOnCreateDefaultEnabled: dataApiRevokeOnCreateDefaultFlag,
}),
-8
View File
@@ -359,14 +359,6 @@ export interface ProjectCreationSimpleVersionSubmittedEvent {
* false = "Postgres" (default)
*/
useOrioleDb?: boolean
/**
* Whether the tableEditorApiAccessToggle PostHog flag was enabled for this user.
* Gates the integrations Data API settings surface only; no longer controls
* project-creation revoke behaviour (see dataApiRevokeOnCreateDefaultEnabled).
* true/false = flag state when project was created
* omitted = PostHog flags had not loaded at the time of project creation
*/
tableEditorApiAccessToggleEnabled?: boolean
/**
* Raw checkbox state for "Automatically expose new tables and functions" at submission.
* true = default privileges are granted on new entities (current behaviour)