Merge pull request #16212 from supabase/hf/add-navigator-lock-feature-flag

feat: add feature flag for enabling GoTrue navigator lock
This commit is contained in:
Stojan Dimitrovski authored and GitHub committed 2023-08-14 09:45:40 +02:00
commit 4b027d1805
5 files changed
+96 -18

No files matched your search

+4 -2
View File
@@ -10,9 +10,11 @@ const debug =
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' &&
globalThis?.localStorage?.getItem(AUTH_DEBUG_KEY) === 'true'
const navigatorLockEnabled =
const navigatorLockEnabled = !!(
process.env.NEXT_PUBLIC_IS_PLATFORM === 'true' &&
globalThis?.localStorage?.getItem(AUTH_NAVIGATOR_LOCK_KEY) === 'true'
globalThis?.localStorage?.getItem(AUTH_NAVIGATOR_LOCK_KEY) === 'true' &&
globalThis?.navigator?.locks
)
export const gotrueClient = new GoTrueClient({
url: process.env.NEXT_PUBLIC_GOTRUE_URL,
+5 -16
View File
@@ -5,8 +5,7 @@ import * as configcat from 'configcat-js'
import { IS_PLATFORM } from 'lib/constants'
import FlagContext from './FlagContext'
import { useUser } from 'lib/auth'
let client: configcat.IConfigCatClient
import { getFlags } from 'lib/configcat'
const FlagProvider = ({ children }: PropsWithChildren<{}>) => {
const user = useUser()
@@ -14,20 +13,10 @@ const FlagProvider = ({ children }: PropsWithChildren<{}>) => {
const { Provider } = FlagContext
const [store, setStore] = useState({})
const getFlags = async (user?: User) => {
if (!client) {
client = configcat.getClient(
process.env.NEXT_PUBLIC_CONFIGCAT_SDK_KEY ?? '',
configcat.PollingMode.AutoPoll,
{ pollIntervalSeconds: 600 }
)
}
const processFlags = async (user?: User) => {
const flagStore: any = {}
const flagValues =
user?.email !== undefined
? await client.getAllValuesAsync(new configcat.User(user.email))
: await client.getAllValuesAsync()
const flagValues = await getFlags(user)
flagValues.forEach((item: any) => {
flagStore[item.settingKey] = item.settingValue
})
@@ -39,7 +28,7 @@ const FlagProvider = ({ children }: PropsWithChildren<{}>) => {
// as per https://configcat.com/docs/sdk-reference/js/#polling-modes:
// The polling downloads the config.json at the set interval and are stored in the internal cache
// which subsequently all getValueAsync() calls are served from there
if (IS_PLATFORM) getFlags(user ?? undefined)
if (IS_PLATFORM) processFlags(user ?? undefined)
}, [user])
return <Provider value={store}>{children}</Provider>
+24
View File
@@ -0,0 +1,24 @@
import { User } from '@supabase/supabase-js'
import * as configcat from 'configcat-js'
let client: configcat.IConfigCatClient
export function getClient() {
if (client) {
return client
}
client = configcat.getClient(
process.env.NEXT_PUBLIC_CONFIGCAT_SDK_KEY ?? '',
configcat.PollingMode.AutoPoll,
{ pollIntervalSeconds: 600 }
)
return client
}
export async function getFlags(user?: User) {
return user?.email !== undefined
? await getClient().getAllValuesAsync(new configcat.User(user.email))
: await getClient().getAllValuesAsync()
}
+37
View File
@@ -1,5 +1,38 @@
import { IS_PLATFORM } from './constants'
import { User } from '@supabase/gotrue-js'
import { gotrueClient } from 'common'
import { getFlags } from './configcat'
import { getNavigatorLockFeatureFlagThreshold, setNavigatorLockEnabled } from './local-storage'
// The first time this file is imported, ConfigCat will be asked for all
// available feature flags. The client that this is running in will have
// determined and saved a random number [0, 100) under
// `supabase.dashboard.ff.threshold.navigatorLock`. If there is a number-valued
// feature flag `navigatorLockThreshold`, the
// `supabase.dashboard.auth.navigatorLock.enabled` localStorage key will be set
// to true if the value chosen by the browser is <= the value in the feature
// flag.On the _following_ refresh of the page, `packages/common/gotrue.ts`
// will read this value and enable the GoTrueClient navigatorLock.
// ConfigCat does not have a native way to do this, as percent-based rollouts
// are only available when ConfigCat has a user ID, and not without one, which
// can be the case here (GoTrue is used when not authenticated too).
async function determineNavigatorLockFeatureFlag() {
const flags = await getFlags()
const value = flags.find((flag) => flag.settingKey === 'navigatorLockThreshold')?.settingValue
if (typeof value === 'number' && value > 0) {
const threshold = getNavigatorLockFeatureFlagThreshold()
if (typeof threshold === 'number') {
setNavigatorLockEnabled(threshold <= value)
} else {
setNavigatorLockEnabled(false)
}
} else {
setNavigatorLockEnabled(false)
}
}
export { STORAGE_KEY } from 'common'
export const auth = gotrueClient
@@ -62,3 +95,7 @@ export const getReturnToPath = (fallback = '/projects') => {
return validReturnTo + (remainingSearchParams ? `?${remainingSearchParams}` : '')
}
if (IS_PLATFORM && globalThis.window) {
determineNavigatorLockFeatureFlag()
}
+26
View File
@@ -6,6 +6,8 @@ export const LOCAL_STORAGE_KEYS_ALLOWLIST = [
'supabaseDarkMode',
'supabase.dashboard.sign_in_clicks',
'supabase.dashboard.auth.debug',
'supabase.dashboard.auth.navigatorLock.enabled',
'supabase.dashboard.auth.ff.threshold.navigatorLock',
]
export function clearLocalStorage() {
@@ -55,7 +57,31 @@ export function resetSignInClicks(): number {
return clicks
}
export function getNavigatorLockFeatureFlagThreshold() {
const str = localStorage.getItem('supabase.dashboard.auth.ff.threshold.navigatorLock')
return str ? parseInt(str) : null
}
function determineNavigatorLockFeatureFlagThreshold() {
if (getNavigatorLockFeatureFlagThreshold()) {
return
}
localStorage.setItem(
'supabase.dashboard.auth.ff.threshold.navigatorLock',
`${Math.floor(Math.random() * 100)}`
)
}
export function setNavigatorLockEnabled(enabled: boolean) {
localStorage.setItem('supabase.dashboard.auth.navigatorLock.enabled', enabled ? 'true' : 'false')
}
if (globalThis && globalThis.localStorage && IS_PLATFORM) {
// populate the value based on the current local storage state
inferSignInClicks()
// setup the navigator lock group on initial load
determineNavigatorLockFeatureFlagThreshold()
}