mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
feat(studio): move SIWC rollout gate to ConfigCat (#48360)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Replace the deploy-gated `dashboard_auth:sign_in_with_chatgpt` AND-gate with useFlag('ShowSignInWithChatGptButton') OR'd against the existing localStorage opt-in switch, so rollout/rollback no longer requires a frontend deploy. Remove the now-dead static flag, its only consumer was this gate. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * ChatGPT sign-in availability now responds to either the local opt-in setting or the updated configuration flag. * GitHub sign-in continues to follow its dedicated feature setting. * **Bug Fixes** * Corrected identity provider visibility across different sign-in configuration combinations. * **Chores** * Removed the obsolete ChatGPT sign-in feature setting. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
69f3c43118
commit
b8310dbf23
4 files changed
+61
-33
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { renderHook } from '@testing-library/react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { useEnabledIdentityProviders } from '../useEnabledIdentityProviders'
|
||||
import {
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
|
||||
const mockIsFeatureEnabled = vi.hoisted(() => vi.fn())
|
||||
const mockUseLocalStorageQuery = vi.hoisted(() => vi.fn())
|
||||
const mockUseFlag = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock('../useIsFeatureEnabled', () => ({
|
||||
useIsFeatureEnabled: mockIsFeatureEnabled,
|
||||
@@ -18,13 +19,22 @@ vi.mock('../useLocalStorage', () => ({
|
||||
useLocalStorageQuery: mockUseLocalStorageQuery,
|
||||
}))
|
||||
|
||||
vi.mock('common', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import('common')>()),
|
||||
useFlag: mockUseFlag,
|
||||
}))
|
||||
|
||||
describe('useEnabledIdentityProviders', () => {
|
||||
beforeEach(() => {
|
||||
mockUseFlag.mockReset()
|
||||
})
|
||||
|
||||
it('returns every provider when all flags are enabled', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({
|
||||
dashboardAuthSignInWithGithub: true,
|
||||
dashboardAuthSignInWithChatgpt: true,
|
||||
})
|
||||
mockUseLocalStorageQuery.mockReturnValue([true])
|
||||
mockUseFlag.mockReturnValue(true)
|
||||
|
||||
const { result } = renderHook(() => useEnabledIdentityProviders())
|
||||
|
||||
@@ -34,45 +44,69 @@ describe('useEnabledIdentityProviders', () => {
|
||||
it('returns no providers when all flags are disabled', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({
|
||||
dashboardAuthSignInWithGithub: false,
|
||||
dashboardAuthSignInWithChatgpt: false,
|
||||
})
|
||||
mockUseLocalStorageQuery.mockReturnValue([false])
|
||||
mockUseFlag.mockReturnValue(false)
|
||||
|
||||
const { result } = renderHook(() => useEnabledIdentityProviders())
|
||||
|
||||
expect(result.current).toEqual([])
|
||||
})
|
||||
|
||||
it('includes ChatGPT when its flag is enabled and the local storage switch is truthy', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({
|
||||
dashboardAuthSignInWithGithub: false,
|
||||
dashboardAuthSignInWithChatgpt: true,
|
||||
})
|
||||
it('includes ChatGPT when localStorage is true and configcat is true', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({ dashboardAuthSignInWithGithub: false })
|
||||
mockUseLocalStorageQuery.mockReturnValue([true])
|
||||
mockUseFlag.mockReturnValue(true)
|
||||
|
||||
const { result } = renderHook(() => useEnabledIdentityProviders())
|
||||
|
||||
expect(result.current).toEqual([CHATGPT_IDENTITY_PROVIDER])
|
||||
})
|
||||
|
||||
it('excludes ChatGPT when its flag is enabled but the local storage switch is unset', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({
|
||||
dashboardAuthSignInWithGithub: false,
|
||||
dashboardAuthSignInWithChatgpt: true,
|
||||
})
|
||||
it('includes ChatGPT when localStorage is true and configcat is false', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({ dashboardAuthSignInWithGithub: false })
|
||||
mockUseLocalStorageQuery.mockReturnValue([true])
|
||||
mockUseFlag.mockReturnValue(false)
|
||||
|
||||
const { result } = renderHook(() => useEnabledIdentityProviders())
|
||||
|
||||
expect(result.current).toEqual([CHATGPT_IDENTITY_PROVIDER])
|
||||
})
|
||||
|
||||
it('includes ChatGPT when localStorage is false and configcat is true', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({ dashboardAuthSignInWithGithub: false })
|
||||
mockUseLocalStorageQuery.mockReturnValue([false])
|
||||
mockUseFlag.mockReturnValue(true)
|
||||
|
||||
const { result } = renderHook(() => useEnabledIdentityProviders())
|
||||
|
||||
expect(result.current).toEqual([CHATGPT_IDENTITY_PROVIDER])
|
||||
})
|
||||
|
||||
it('excludes ChatGPT when localStorage is false and configcat is false', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({ dashboardAuthSignInWithGithub: false })
|
||||
mockUseLocalStorageQuery.mockReturnValue([false])
|
||||
mockUseFlag.mockReturnValue(false)
|
||||
|
||||
const { result } = renderHook(() => useEnabledIdentityProviders())
|
||||
|
||||
expect(result.current).toEqual([])
|
||||
})
|
||||
|
||||
it('excludes ChatGPT when the local storage switch is truthy but its flag is disabled', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({
|
||||
dashboardAuthSignInWithGithub: false,
|
||||
dashboardAuthSignInWithChatgpt: false,
|
||||
})
|
||||
mockUseLocalStorageQuery.mockReturnValue([true])
|
||||
it('includes GitHub when its feature flag is enabled', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({ dashboardAuthSignInWithGithub: true })
|
||||
mockUseLocalStorageQuery.mockReturnValue([false])
|
||||
mockUseFlag.mockReturnValue(false)
|
||||
|
||||
const { result } = renderHook(() => useEnabledIdentityProviders())
|
||||
|
||||
expect(result.current).toEqual([GITHUB_IDENTITY_PROVIDER])
|
||||
})
|
||||
|
||||
it('excludes GitHub when its feature flag is disabled', () => {
|
||||
mockIsFeatureEnabled.mockReturnValue({ dashboardAuthSignInWithGithub: false })
|
||||
mockUseLocalStorageQuery.mockReturnValue([false])
|
||||
mockUseFlag.mockReturnValue(false)
|
||||
|
||||
const { result } = renderHook(() => useEnabledIdentityProviders())
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { LOCAL_STORAGE_KEYS, useFlag } from 'common'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useIsFeatureEnabled } from './useIsFeatureEnabled'
|
||||
@@ -14,28 +14,28 @@ import {
|
||||
* To add a provider: declare its config in `lib/external-identity-providers.ts`, add a
|
||||
* `dashboard_auth:sign_in_with_*` flag, and gate it here.
|
||||
*
|
||||
* ChatGPT is a deliberate exception: it's also gated behind a manual, localStorage-only rollout
|
||||
* switch (`LOCAL_STORAGE_KEYS.SIGN_IN_CHATGPT_ENABLED`) on top of its feature flag, since it's WIP.
|
||||
* ChatGPT is a deliberate exception: it's rolled out via the `ShowSignInWithChatGptButton`
|
||||
* ConfigCat flag OR'd with a manual, localStorage-only opt-in switch
|
||||
* (`LOCAL_STORAGE_KEYS.SIGN_IN_CHATGPT_ENABLED`, flippable via the `?siwc-enabled=1` query param —
|
||||
* see `useSiwcQueryParamOptIn`), instead of the static `dashboard_auth:sign_in_with_*` pattern.
|
||||
*/
|
||||
export function useEnabledIdentityProviders(): ExternalIdentityProviderConfig[] {
|
||||
const { dashboardAuthSignInWithGithub: githubEnabled } = useIsFeatureEnabled([
|
||||
'dashboard_auth:sign_in_with_github',
|
||||
])
|
||||
|
||||
const { dashboardAuthSignInWithChatgpt: chatgptEnabled } = useIsFeatureEnabled([
|
||||
'dashboard_auth:sign_in_with_chatgpt',
|
||||
])
|
||||
const [chatgptLocalStorageEnabled] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.SIGN_IN_CHATGPT_ENABLED,
|
||||
false
|
||||
)
|
||||
const chatGptConfigCatFlagEnabled = useFlag('ShowSignInWithChatGptButton')
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
[
|
||||
githubEnabled && GITHUB_IDENTITY_PROVIDER,
|
||||
chatgptEnabled && chatgptLocalStorageEnabled && CHATGPT_IDENTITY_PROVIDER,
|
||||
(chatgptLocalStorageEnabled || chatGptConfigCatFlagEnabled) && CHATGPT_IDENTITY_PROVIDER,
|
||||
].filter((p): p is ExternalIdentityProviderConfig => Boolean(p)),
|
||||
[githubEnabled, chatgptEnabled, chatgptLocalStorageEnabled]
|
||||
[githubEnabled, chatgptLocalStorageEnabled, chatGptConfigCatFlagEnabled]
|
||||
)
|
||||
}
|
||||
@@ -34,7 +34,6 @@
|
||||
|
||||
"dashboard_auth:sign_up": true,
|
||||
"dashboard_auth:sign_in_with_github": true,
|
||||
"dashboard_auth:sign_in_with_chatgpt": true,
|
||||
"dashboard_auth:sign_in_with_sso": true,
|
||||
"dashboard_auth:sign_in_with_email": true,
|
||||
"dashboard_auth:show_testimonial": true,
|
||||
|
||||
@@ -115,10 +115,6 @@
|
||||
"type": "boolean",
|
||||
"description": "Enable the sign in with github provider"
|
||||
},
|
||||
"dashboard_auth:sign_in_with_chatgpt": {
|
||||
"type": "boolean",
|
||||
"description": "Enable the sign in with chatgpt provider"
|
||||
},
|
||||
"dashboard_auth:sign_in_with_sso": {
|
||||
"type": "boolean",
|
||||
"description": "Enable the sign in with sso provider"
|
||||
@@ -472,7 +468,6 @@
|
||||
"billing:all",
|
||||
"dashboard_auth:sign_up",
|
||||
"dashboard_auth:sign_in_with_github",
|
||||
"dashboard_auth:sign_in_with_chatgpt",
|
||||
"dashboard_auth:sign_in_with_sso",
|
||||
"dashboard_auth:sign_in_with_email",
|
||||
"dashboard_auth:show_tos",
|
||||
|
||||
Reference in new issue
Block a user