-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Issue #3249] Add auth ui feature flag and refactor feature flag syst…
…em for client side use (#3374) * rewrites the FeatureFlagManager and useFeatureFlag hook to allow syncing flags between server and client using cookies * adds the `authOn` feature flag in frontend code and terraform * refactors the `environments` setup a bit to more easily expose feature flags * splits functionality that does not benefit from being held in the FeatureFlagsManager class into a helper file * moves feature flag manager file into a nested directory
- Loading branch information
1 parent
f7c1248
commit f73dffe
Showing
22 changed files
with
1,019 additions
and
977 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export type FeatureFlags = { [name: string]: boolean }; | ||
|
||
export const defaultFeatureFlags: FeatureFlags = { | ||
// Kill switches for search and opportunity pages, will show maintenance page when turned on | ||
searchOff: false, | ||
opportunityOff: false, | ||
// should we show a sign in button in the header? | ||
authOn: false, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,44 @@ | ||
import { stringToBoolean } from "src/utils/generalUtils"; | ||
|
||
const { | ||
NEXT_PUBLIC_BASE_PATH, | ||
USE_SEARCH_MOCK_DATA = "", | ||
USE_SEARCH_MOCK_DATA, | ||
SENDY_API_URL, | ||
SENDY_API_KEY, | ||
SENDY_LIST_ID, | ||
API_URL, | ||
API_AUTH_TOKEN = "", | ||
API_AUTH_TOKEN, | ||
NEXT_BUILD, | ||
SESSION_SECRET, | ||
NEXT_PUBLIC_BASE_URL, | ||
FEATURE_SEARCH_OFF = "false", | ||
FEATURE_OPPORTUNITY_OFF = "false", | ||
NEXT_BUILD = "false", | ||
ENVIRONMENT = "dev", | ||
SESSION_SECRET = "", | ||
FEATURE_SEARCH_OFF, | ||
FEATURE_OPPORTUNITY_OFF, | ||
FEATURE_AUTH_ON, | ||
AUTH_LOGIN_URL, | ||
} = process.env; | ||
|
||
export const featureFlags = { | ||
opportunityOff: stringToBoolean(FEATURE_OPPORTUNITY_OFF), | ||
searchOff: stringToBoolean(FEATURE_SEARCH_OFF), | ||
authOn: stringToBoolean(FEATURE_AUTH_ON), | ||
}; | ||
|
||
// home for all interpreted server side environment variables | ||
export const environment: { [key: string]: string } = { | ||
LEGACY_HOST: | ||
ENVIRONMENT === "prod" ? "https://grants.gov" : "https://test.grants.gov", | ||
NEXT_PUBLIC_BASE_PATH: NEXT_PUBLIC_BASE_PATH ?? "", | ||
USE_SEARCH_MOCK_DATA, | ||
USE_SEARCH_MOCK_DATA: USE_SEARCH_MOCK_DATA || "", | ||
SENDY_API_URL: SENDY_API_URL || "", | ||
SENDY_API_KEY: SENDY_API_KEY || "", | ||
SENDY_LIST_ID: SENDY_LIST_ID || "", | ||
API_URL: API_URL || "", | ||
API_AUTH_TOKEN, | ||
AUTH_LOGIN_URL: AUTH_LOGIN_URL || "", | ||
NEXT_PUBLIC_BASE_URL: NEXT_PUBLIC_BASE_URL || "http://localhost:3000", | ||
API_AUTH_TOKEN: API_AUTH_TOKEN || "", | ||
GOOGLE_TAG_MANAGER_ID: "GTM-MV57HMHS", | ||
FEATURE_OPPORTUNITY_OFF, | ||
FEATURE_SEARCH_OFF, | ||
NEXT_BUILD, | ||
ENVIRONMENT, | ||
SESSION_SECRET, | ||
NEXT_BUILD: NEXT_BUILD || "false", | ||
SESSION_SECRET: SESSION_SECRET || "", | ||
NEXT_PUBLIC_BASE_URL: NEXT_PUBLIC_BASE_URL || "http://localhost:3000", | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,70 @@ | ||
"use client"; | ||
|
||
import Cookies from "js-cookie"; | ||
import { FeatureFlagsManager } from "src/services/FeatureFlagManager"; | ||
import { isBoolean } from "lodash"; | ||
import { | ||
defaultFeatureFlags, | ||
FeatureFlags, | ||
} from "src/constants/defaultFeatureFlags"; | ||
import { | ||
FEATURE_FLAGS_KEY, | ||
getCookieExpiration, | ||
} from "src/services/featureFlags/featureFlagHelpers"; | ||
|
||
import { useEffect, useState } from "react"; | ||
import { useCallback, useEffect, useState } from "react"; | ||
|
||
/** | ||
* React hook for reading and managing feature flags in client-side code. | ||
* | ||
* ``` | ||
* function MyComponent() { | ||
* const { | ||
* featureFlagsManager, // An instance of FeatureFlagsManager | ||
* mounted, // Useful for hydration | ||
* setFeatureFlag, // Proxy for featureFlagsManager.setFeatureFlagCookie that handles updating state | ||
* } = useFeatureFlags() | ||
* | ||
* if (featureFlagsManager.isFeatureEnabled("someFeatureFlag")) { | ||
* // Do something | ||
* } | ||
* | ||
* if (!mounted) { | ||
* // To allow hydration | ||
* return null | ||
* } | ||
* Allows client components to access feature flags by | ||
* - setting the cookie | ||
* - reading the cookie | ||
* | ||
* return ( | ||
* ... | ||
* ) | ||
* } | ||
* ``` | ||
*/ | ||
export function useFeatureFlags() { | ||
const [featureFlagsManager, setFeatureFlagsManager] = useState( | ||
new FeatureFlagsManager(Cookies), | ||
); | ||
const [mounted, setMounted] = useState(false); | ||
export function useFeatureFlags(): { | ||
setFeatureFlag: (flagName: string, value: boolean) => void; | ||
checkFeatureFlag: (flagName: string) => boolean; | ||
featureFlags: FeatureFlags; | ||
} { | ||
const [featureFlags, setFeatureFlags] = | ||
useState<FeatureFlags>(defaultFeatureFlags); | ||
|
||
// a workaround, as setting this in default state value results in hydration error | ||
useEffect(() => { | ||
setMounted(true); | ||
const flagsFromCookie = JSON.parse( | ||
Cookies.get(FEATURE_FLAGS_KEY) || "{}", | ||
) as FeatureFlags; | ||
setFeatureFlags(flagsFromCookie); | ||
}, []); | ||
|
||
function setFeatureFlag(name: string, value: boolean) { | ||
featureFlagsManager.setFeatureFlagCookie(name, value); | ||
setFeatureFlagsManager(new FeatureFlagsManager(Cookies)); | ||
} | ||
// Note that values set in cookies will be persistent per browser session unless explicitly overwritten | ||
const setFeatureFlag = useCallback( | ||
(flagName: string, value: boolean) => { | ||
const newFlags = { | ||
...featureFlags, | ||
[flagName]: value, | ||
}; | ||
setFeatureFlags(newFlags); | ||
Cookies.set(FEATURE_FLAGS_KEY, JSON.stringify(newFlags), { | ||
expires: getCookieExpiration(), | ||
}); | ||
}, | ||
[featureFlags, setFeatureFlags], | ||
); | ||
|
||
const checkFeatureFlag = useCallback( | ||
(flagName: string): boolean => { | ||
const value = featureFlags[flagName]; | ||
if (!isBoolean(value)) { | ||
console.error("Unknown or misconfigured feature flag: ", flagName); | ||
return false; | ||
} | ||
return value; | ||
}, | ||
[featureFlags], | ||
); | ||
|
||
return { featureFlagsManager, mounted, setFeatureFlag }; | ||
return { | ||
setFeatureFlag, | ||
checkFeatureFlag, | ||
featureFlags, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.