mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
## 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? Refactor / performance. ## What is the current behavior? `packages/common/configcat.ts` does a two-step setup: a probe `fetch` to the ConfigCat proxy URL, followed by SDK client initialization with the proxy as `baseUrl` (or against the direct ConfigCat CDN if the probe fails). On the happy path this fires **two** network requests for the same JSON config on cold start — the probe, then the SDK's own initial AutoPoll fetch. ## What is the new behavior? The probe is removed. We initialize the proxy client directly and inspect the `ClientCacheState` returned by `waitForReady()`. On `NoFlagData` (proxy unreachable, no cache) we `dispose()` the proxy client and fall back to the direct SDK key client. Cold-start fetches drop from 2 to 1 when the proxy is healthy. Worst-case fallback delay is bounded by `maxInitWaitTimeSeconds` (5s default), comparable to today's probe timeout on a broken proxy. The unused exported \`fetchHandler\` is removed (no external importers — verified via grep). Tests in \`configcat.test.ts\` are updated to mock \`waitForReady\`/\`dispose\` and a new test covers the proxy-failure fallback path. ## Additional context Resolves FE-3174 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved ConfigCat client initialization with robust fallback handling when proxy is unavailable. * **Chores** * Removed `fetchHandler` export from ConfigCat module. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45939) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
81 lines
2.5 KiB
TypeScript
81 lines
2.5 KiB
TypeScript
import * as configcat from 'configcat-js'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
vi.mock('configcat-js', () => ({
|
|
getClient: vi.fn(),
|
|
PollingMode: {
|
|
AutoPoll: 'AutoPoll',
|
|
},
|
|
ClientCacheState: {
|
|
NoFlagData: 0,
|
|
HasLocalOverrideFlagDataOnly: 1,
|
|
HasCachedFlagDataOnly: 2,
|
|
HasUpToDateFlagData: 3,
|
|
},
|
|
User: vi.fn(),
|
|
}))
|
|
|
|
describe('configcat', () => {
|
|
const mockClient = {
|
|
getAllValuesAsync: vi.fn(),
|
|
waitForReady: vi.fn().mockResolvedValue(3), // HasUpToDateFlagData
|
|
dispose: vi.fn(),
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
vi.resetModules()
|
|
vi.unstubAllEnvs()
|
|
;(configcat.getClient as any).mockReturnValue(mockClient)
|
|
})
|
|
|
|
it('should return empty array when no email is provided', async () => {
|
|
const { getFlags } = await import('./configcat')
|
|
const result = await getFlags()
|
|
expect(result).toEqual([])
|
|
})
|
|
|
|
it('should call getAllValuesAsync with user when email is provided', async () => {
|
|
vi.stubEnv('NEXT_PUBLIC_CONFIGCAT_PROXY_URL', 'https://proxy.example.com')
|
|
|
|
const { getFlags } = await import('./configcat')
|
|
|
|
const email = 'test@example.com'
|
|
const mockValues = { flag1: true, flag2: false }
|
|
mockClient.getAllValuesAsync.mockResolvedValue(mockValues)
|
|
|
|
const result = await getFlags(email)
|
|
|
|
expect(configcat.User).toHaveBeenCalledWith(email, undefined, undefined, expect.any(Object))
|
|
expect(mockClient.getAllValuesAsync).toHaveBeenCalled()
|
|
expect(result).toEqual(mockValues)
|
|
})
|
|
|
|
it('should fall back to SDK key client when proxy returns NoFlagData', async () => {
|
|
vi.stubEnv('NEXT_PUBLIC_CONFIGCAT_PROXY_URL', 'https://proxy.example.com')
|
|
vi.stubEnv('NEXT_PUBLIC_CONFIGCAT_SDK_KEY', 'test-sdk-key')
|
|
|
|
const proxyClient = {
|
|
getAllValuesAsync: vi.fn(),
|
|
waitForReady: vi.fn().mockResolvedValue(0), // NoFlagData
|
|
dispose: vi.fn(),
|
|
}
|
|
const sdkClient = {
|
|
getAllValuesAsync: vi.fn().mockResolvedValue([]),
|
|
waitForReady: vi.fn().mockResolvedValue(3), // HasUpToDateFlagData
|
|
dispose: vi.fn(),
|
|
}
|
|
|
|
;(configcat.getClient as any).mockReturnValueOnce(proxyClient).mockReturnValueOnce(sdkClient)
|
|
|
|
const { getFlags } = await import('./configcat')
|
|
await getFlags()
|
|
|
|
expect(configcat.getClient).toHaveBeenCalledTimes(2)
|
|
expect(proxyClient.dispose).toHaveBeenCalled()
|
|
expect(configcat.getClient).toHaveBeenNthCalledWith(2, 'test-sdk-key', 'AutoPoll', {
|
|
pollIntervalSeconds: 7 * 60,
|
|
})
|
|
})
|
|
})
|