feat: frontend consumption of feature flags (#17926)
parent
4fba49fde4
commit
22c7d09b70
|
|
@ -1,5 +1,6 @@
|
|||
// Libraries
|
||||
import {FC, useEffect} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {withRouter, WithRouterProps} from 'react-router'
|
||||
|
||||
// APIs
|
||||
|
|
@ -10,8 +11,14 @@ import {CLOUD, CLOUD_URL, CLOUD_LOGOUT_PATH} from 'src/shared/constants'
|
|||
|
||||
// Components
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {reset} from 'src/shared/actions/flags'
|
||||
|
||||
const Logout: FC<WithRouterProps> = ({router}) => {
|
||||
interface DispatchProps {
|
||||
resetFeatureFlags: typeof reset
|
||||
}
|
||||
|
||||
type Props = DispatchProps & WithRouterProps
|
||||
const Logout: FC<Props> = ({router, resetFeatureFlags}) => {
|
||||
const handleSignOut = async () => {
|
||||
if (CLOUD) {
|
||||
window.location.href = `${CLOUD_URL}${CLOUD_LOGOUT_PATH}`
|
||||
|
|
@ -28,9 +35,19 @@ const Logout: FC<WithRouterProps> = ({router}) => {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
resetFeatureFlags()
|
||||
handleSignOut()
|
||||
}, [])
|
||||
return null
|
||||
}
|
||||
|
||||
export default ErrorHandling(withRouter<WithRouterProps>(Logout))
|
||||
const mdtp = {
|
||||
resetFeatureFlags: reset,
|
||||
}
|
||||
|
||||
export default ErrorHandling(
|
||||
connect<{}, DispatchProps>(
|
||||
null,
|
||||
mdtp
|
||||
)(withRouter<WithRouterProps>(Logout))
|
||||
)
|
||||
|
|
|
|||
471
ui/src/index.tsx
471
ui/src/index.tsx
|
|
@ -38,6 +38,7 @@ import {MePage} from 'src/me'
|
|||
import NotFound from 'src/shared/components/NotFound'
|
||||
import GetLinks from 'src/shared/containers/GetLinks'
|
||||
import GetMe from 'src/shared/containers/GetMe'
|
||||
import GetFlags from 'src/shared/containers/GetFlags'
|
||||
import UnauthenticatedApp from 'src/shared/containers/UnauthenticatedApp'
|
||||
import TaskExportOverlay from 'src/tasks/components/TaskExportOverlay'
|
||||
import TaskImportOverlay from 'src/tasks/components/TaskImportOverlay'
|
||||
|
|
@ -201,258 +202,278 @@ class Root extends PureComponent {
|
|||
</Route>
|
||||
<Route component={Signin}>
|
||||
<Route component={GetMe}>
|
||||
<Route component={GetOrganizations}>
|
||||
<Route path="/">
|
||||
<Route path="no-orgs" component={NoOrgsPage} />
|
||||
<IndexRoute component={RouteToOrg} />
|
||||
<Route path="orgs" component={App}>
|
||||
<Route path="new" component={CreateOrgOverlay} />
|
||||
<Route path=":orgID" component={SetOrg}>
|
||||
<IndexRoute component={MePage} />
|
||||
<Route path="tasks" component={TasksPage}>
|
||||
<Route
|
||||
path=":id/export"
|
||||
component={TaskExportOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="import"
|
||||
component={TaskImportOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="import/template"
|
||||
component={TaskImportFromTemplateOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="tasks/:id/runs"
|
||||
component={TaskRunsPage}
|
||||
/>
|
||||
<Route path="tasks/new" component={TaskPage} />
|
||||
<Route path="tasks/:id" component={TaskEditPage} />
|
||||
<Route
|
||||
path="data-explorer"
|
||||
component={DataExplorerPage}
|
||||
>
|
||||
<Route path="save" component={SaveAsOverlay} />
|
||||
<Route
|
||||
path="delete-data"
|
||||
component={DEDeleteDataOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="dashboards" component={DashboardsIndex}>
|
||||
<Route
|
||||
path="import"
|
||||
component={DashboardImportOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="import/template"
|
||||
component={CreateFromTemplateOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":dashboardID/export"
|
||||
component={DashboardExportOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="dashboards/:dashboardID"
|
||||
component={DashboardContainer}
|
||||
>
|
||||
<Route path="cells">
|
||||
<Route path="new" component={NewVEO} />
|
||||
<Route path=":cellID/edit" component={EditVEO} />
|
||||
</Route>
|
||||
<Route path="notes">
|
||||
<Route path="new" component={AddNoteOverlay} />
|
||||
<Route component={GetFlags}>
|
||||
<Route component={GetOrganizations}>
|
||||
<Route path="/">
|
||||
<Route path="no-orgs" component={NoOrgsPage} />
|
||||
<IndexRoute component={RouteToOrg} />
|
||||
<Route path="orgs" component={App}>
|
||||
<Route path="new" component={CreateOrgOverlay} />
|
||||
<Route path=":orgID" component={SetOrg}>
|
||||
<IndexRoute component={MePage} />
|
||||
<Route path="tasks" component={TasksPage}>
|
||||
<Route
|
||||
path=":cellID/edit"
|
||||
component={EditNoteOverlay}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="me" component={MePage} />
|
||||
<Route path="load-data">
|
||||
<IndexRoute component={BucketsIndex} />
|
||||
<Route path="tokens" component={TokensIndex}>
|
||||
<Route path="generate">
|
||||
<Route
|
||||
path="all-access"
|
||||
component={AllAccessTokenOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="buckets"
|
||||
component={BucketsTokenOverlay}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="buckets" component={BucketsIndex}>
|
||||
<Route path=":bucketID">
|
||||
<Route
|
||||
path="line-protocols/new"
|
||||
component={LineProtocolWizard}
|
||||
/>
|
||||
<Route
|
||||
path="telegrafs/new"
|
||||
component={CollectorsWizard}
|
||||
/>
|
||||
<Route
|
||||
path="scrapers/new"
|
||||
component={CreateScraperOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="edit"
|
||||
component={UpdateBucketOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="delete-data"
|
||||
component={BucketsDeleteDataOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="rename"
|
||||
component={RenameBucketOverlay}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="telegrafs" component={TelegrafsPage}>
|
||||
<Route
|
||||
path=":id/view"
|
||||
component={TelegrafConfigOverlay}
|
||||
path=":id/export"
|
||||
component={TaskExportOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/instructions"
|
||||
component={TelegrafInstructionsOverlay}
|
||||
path="import"
|
||||
component={TaskImportOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="output"
|
||||
component={TelegrafOutputOverlay}
|
||||
/>
|
||||
<Route path="new" component={CollectorsWizard} />
|
||||
</Route>
|
||||
<Route path="scrapers" component={ScrapersIndex}>
|
||||
<Route
|
||||
path="new"
|
||||
component={CreateScraperOverlay}
|
||||
path="import/template"
|
||||
component={TaskImportFromTemplateOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="client-libraries"
|
||||
component={ClientLibrariesPage}
|
||||
path="tasks/:id/runs"
|
||||
component={TaskRunsPage}
|
||||
/>
|
||||
<Route path="tasks/new" component={TaskPage} />
|
||||
<Route path="tasks/:id" component={TaskEditPage} />
|
||||
<Route
|
||||
path="data-explorer"
|
||||
component={DataExplorerPage}
|
||||
>
|
||||
<Route path="save" component={SaveAsOverlay} />
|
||||
<Route
|
||||
path="delete-data"
|
||||
component={DEDeleteDataOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="dashboards"
|
||||
component={DashboardsIndex}
|
||||
>
|
||||
<Route
|
||||
path="csharp"
|
||||
component={ClientCSharpOverlay}
|
||||
/>
|
||||
<Route path="go" component={ClientGoOverlay} />
|
||||
<Route
|
||||
path="java"
|
||||
component={ClientJavaOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="javascript-node"
|
||||
component={ClientJSOverlay}
|
||||
/>
|
||||
<Route path="php" component={ClientPHPOverlay} />
|
||||
<Route
|
||||
path="python"
|
||||
component={ClientPythonOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="ruby"
|
||||
component={ClientRubyOverlay}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="settings">
|
||||
<IndexRoute component={VariablesIndex} />
|
||||
<Route path="variables" component={VariablesIndex}>
|
||||
<Route
|
||||
path="import"
|
||||
component={VariableImportOverlay}
|
||||
component={DashboardImportOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/export"
|
||||
component={VariableExportOverlay}
|
||||
path="import/template"
|
||||
component={CreateFromTemplateOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="new"
|
||||
component={CreateVariableOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/rename"
|
||||
component={RenameVariableOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/edit"
|
||||
component={UpdateVariableOverlay}
|
||||
path=":dashboardID/export"
|
||||
component={DashboardExportOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="templates" component={TemplatesIndex}>
|
||||
<Route
|
||||
path="dashboards/:dashboardID"
|
||||
component={DashboardContainer}
|
||||
>
|
||||
<Route path="cells">
|
||||
<Route path="new" component={NewVEO} />
|
||||
<Route
|
||||
path=":cellID/edit"
|
||||
component={EditVEO}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="notes">
|
||||
<Route path="new" component={AddNoteOverlay} />
|
||||
<Route
|
||||
path=":cellID/edit"
|
||||
component={EditNoteOverlay}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="me" component={MePage} />
|
||||
<Route path="load-data">
|
||||
<IndexRoute component={BucketsIndex} />
|
||||
<Route path="tokens" component={TokensIndex}>
|
||||
<Route path="generate">
|
||||
<Route
|
||||
path="all-access"
|
||||
component={AllAccessTokenOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="buckets"
|
||||
component={BucketsTokenOverlay}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="buckets" component={BucketsIndex}>
|
||||
<Route path=":bucketID">
|
||||
<Route
|
||||
path="line-protocols/new"
|
||||
component={LineProtocolWizard}
|
||||
/>
|
||||
<Route
|
||||
path="telegrafs/new"
|
||||
component={CollectorsWizard}
|
||||
/>
|
||||
<Route
|
||||
path="scrapers/new"
|
||||
component={CreateScraperOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="edit"
|
||||
component={UpdateBucketOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="delete-data"
|
||||
component={BucketsDeleteDataOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="rename"
|
||||
component={RenameBucketOverlay}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="telegrafs" component={TelegrafsPage}>
|
||||
<Route
|
||||
path=":id/view"
|
||||
component={TelegrafConfigOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/instructions"
|
||||
component={TelegrafInstructionsOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="output"
|
||||
component={TelegrafOutputOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="new"
|
||||
component={CollectorsWizard}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="scrapers" component={ScrapersIndex}>
|
||||
<Route
|
||||
path="new"
|
||||
component={CreateScraperOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="import"
|
||||
component={TemplateImportOverlay}
|
||||
path="client-libraries"
|
||||
component={ClientLibrariesPage}
|
||||
>
|
||||
<Route
|
||||
path="csharp"
|
||||
component={ClientCSharpOverlay}
|
||||
/>
|
||||
<Route path="go" component={ClientGoOverlay} />
|
||||
<Route
|
||||
path="java"
|
||||
component={ClientJavaOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="javascript-node"
|
||||
component={ClientJSOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="php"
|
||||
component={ClientPHPOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="python"
|
||||
component={ClientPythonOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="ruby"
|
||||
component={ClientRubyOverlay}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="settings">
|
||||
<IndexRoute component={VariablesIndex} />
|
||||
<Route
|
||||
path="variables"
|
||||
component={VariablesIndex}
|
||||
>
|
||||
<Route
|
||||
path="import"
|
||||
component={VariableImportOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/export"
|
||||
component={VariableExportOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="new"
|
||||
component={CreateVariableOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/rename"
|
||||
component={RenameVariableOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/edit"
|
||||
component={UpdateVariableOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="templates"
|
||||
component={TemplatesIndex}
|
||||
>
|
||||
<Route
|
||||
path="import"
|
||||
component={TemplateImportOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/export"
|
||||
component={TemplateExportOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/view"
|
||||
component={TemplateViewOverlay}
|
||||
/>
|
||||
<Route
|
||||
path=":id/static/view"
|
||||
component={StaticTemplateViewOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="labels" component={LabelsIndex} />
|
||||
<Route path="about" component={OrgProfilePage}>
|
||||
<Route
|
||||
path="rename"
|
||||
component={RenameOrgOverlay}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="alerting" component={AlertingIndex}>
|
||||
<Route
|
||||
path="checks/new-threshold"
|
||||
component={NewThresholdCheckEO}
|
||||
/>
|
||||
<Route
|
||||
path=":id/export"
|
||||
component={TemplateExportOverlay}
|
||||
path="checks/new-deadman"
|
||||
component={NewDeadmanCheckEO}
|
||||
/>
|
||||
<Route
|
||||
path=":id/view"
|
||||
component={TemplateViewOverlay}
|
||||
path="checks/:checkID/edit"
|
||||
component={EditCheckEO}
|
||||
/>
|
||||
<Route
|
||||
path=":id/static/view"
|
||||
component={StaticTemplateViewOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="labels" component={LabelsIndex} />
|
||||
<Route path="about" component={OrgProfilePage}>
|
||||
<Route
|
||||
path="rename"
|
||||
component={RenameOrgOverlay}
|
||||
path="rules/new"
|
||||
component={NewRuleOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="rules/:ruleID/edit"
|
||||
component={EditRuleOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="endpoints/new"
|
||||
component={NewEndpointOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="endpoints/:endpointID/edit"
|
||||
component={EditEndpointOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="alert-history"
|
||||
component={AlertHistoryIndex}
|
||||
/>
|
||||
<Route
|
||||
path="checks/:checkID"
|
||||
component={CheckHistory}
|
||||
/>
|
||||
<Route path="about" component={OrgProfilePage} />
|
||||
{!CLOUD && (
|
||||
<Route path="members" component={MembersIndex} />
|
||||
)}
|
||||
</Route>
|
||||
<Route path="alerting" component={AlertingIndex}>
|
||||
<Route
|
||||
path="checks/new-threshold"
|
||||
component={NewThresholdCheckEO}
|
||||
/>
|
||||
<Route
|
||||
path="checks/new-deadman"
|
||||
component={NewDeadmanCheckEO}
|
||||
/>
|
||||
<Route
|
||||
path="checks/:checkID/edit"
|
||||
component={EditCheckEO}
|
||||
/>
|
||||
<Route
|
||||
path="rules/new"
|
||||
component={NewRuleOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="rules/:ruleID/edit"
|
||||
component={EditRuleOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="endpoints/new"
|
||||
component={NewEndpointOverlay}
|
||||
/>
|
||||
<Route
|
||||
path="endpoints/:endpointID/edit"
|
||||
component={EditEndpointOverlay}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="alert-history"
|
||||
component={AlertHistoryIndex}
|
||||
/>
|
||||
<Route
|
||||
path="checks/:checkID"
|
||||
component={CheckHistory}
|
||||
/>
|
||||
<Route path="about" component={OrgProfilePage} />
|
||||
{!CLOUD && (
|
||||
<Route path="members" component={MembersIndex} />
|
||||
)}
|
||||
</Route>
|
||||
</Route>
|
||||
</Route>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export const localState: LocalStorage = {
|
|||
},
|
||||
},
|
||||
flags: {
|
||||
status: RemoteDataState.Done,
|
||||
original: {},
|
||||
override: {},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,19 +1,33 @@
|
|||
import {Dispatch} from 'redux'
|
||||
import {getFlags as getFlagsRequest} from 'src/client'
|
||||
import {FlagMap} from 'src/shared/reducers/flags'
|
||||
import {RemoteDataState} from 'src/types'
|
||||
export const SET_FEATURE_FLAGS = 'SET_FEATURE_FLAGS'
|
||||
export const RESET_FEATURE_FLAGS = 'RESET_FEATURE_FLAGS'
|
||||
export const CLEAR_FEATURE_FLAG_OVERRIDES = 'CLEAR_FEATURE_FLAG_OVERRIDES'
|
||||
export const SET_FEATURE_FLAG_OVERRIDE = 'SET_FEATURE_FLAG_OVERRIDE'
|
||||
|
||||
export type Actions =
|
||||
| ReturnType<typeof setFlags>
|
||||
| ReturnType<typeof reset>
|
||||
| ReturnType<typeof clearOverrides>
|
||||
| ReturnType<typeof setOverride>
|
||||
|
||||
// NOTE: this doesnt have a type as it will be determined
|
||||
// by the backend at a later time and keeping the format
|
||||
// open for transformations in a bit
|
||||
export const setFlags = flags =>
|
||||
export const setFlags = (status: RemoteDataState, flags?: FlagMap) =>
|
||||
({
|
||||
type: SET_FEATURE_FLAGS,
|
||||
payload: flags,
|
||||
payload: {
|
||||
status,
|
||||
flags,
|
||||
},
|
||||
} as const)
|
||||
|
||||
export const reset = () =>
|
||||
({
|
||||
type: RESET_FEATURE_FLAGS,
|
||||
} as const)
|
||||
|
||||
export const clearOverrides = () =>
|
||||
|
|
@ -28,3 +42,23 @@ export const setOverride = (flag: string, value: string | boolean) =>
|
|||
[flag]: value,
|
||||
},
|
||||
} as const)
|
||||
|
||||
export const getFlags = () => async (
|
||||
dispatch: Dispatch<Actions>
|
||||
): Promise<FlagMap> => {
|
||||
try {
|
||||
dispatch(setFlags(RemoteDataState.Loading))
|
||||
const resp = await getFlagsRequest({})
|
||||
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(resp.data.message)
|
||||
}
|
||||
|
||||
dispatch(setFlags(RemoteDataState.Done, resp.data))
|
||||
|
||||
return resp.data
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(setFlags(RemoteDataState.Error, null))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
// Libraries
|
||||
import React, {useEffect, FunctionComponent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
// Components
|
||||
import {SpinnerContainer, TechnoSpinner} from '@influxdata/clockface'
|
||||
|
||||
// Types
|
||||
import {RemoteDataState, AppState} from 'src/types'
|
||||
|
||||
// Actions
|
||||
import {getFlags as getFlagsAction} from 'src/shared/actions/flags'
|
||||
|
||||
interface PassedInProps {
|
||||
children: React.ReactElement<any>
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
getFlags: typeof getFlagsAction
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
status: RemoteDataState
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps & PassedInProps
|
||||
|
||||
const GetFlags: FunctionComponent<Props> = ({status, getFlags, children}) => {
|
||||
useEffect(() => {
|
||||
if (status === RemoteDataState.NotStarted) {
|
||||
getFlags()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<SpinnerContainer loading={status} spinnerComponent={<TechnoSpinner />}>
|
||||
{children && React.cloneElement(children)}
|
||||
</SpinnerContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const mdtp = {
|
||||
getFlags: getFlagsAction,
|
||||
}
|
||||
|
||||
const mstp = (state: AppState): StateProps => ({
|
||||
status: state.flags.status || RemoteDataState.NotStarted,
|
||||
})
|
||||
|
||||
export default connect<StateProps, DispatchProps, PassedInProps>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(GetFlags)
|
||||
|
|
@ -1,20 +1,24 @@
|
|||
import {
|
||||
Actions,
|
||||
SET_FEATURE_FLAGS,
|
||||
RESET_FEATURE_FLAGS,
|
||||
CLEAR_FEATURE_FLAG_OVERRIDES,
|
||||
SET_FEATURE_FLAG_OVERRIDE,
|
||||
} from 'src/shared/actions/flags'
|
||||
import {RemoteDataState} from 'src/types'
|
||||
|
||||
export interface FlagMap {
|
||||
[key: string]: string | boolean
|
||||
}
|
||||
|
||||
export interface FlagState {
|
||||
status: RemoteDataState
|
||||
original: FlagMap
|
||||
override: FlagMap
|
||||
}
|
||||
|
||||
const defaultState: FlagState = {
|
||||
status: RemoteDataState.NotStarted,
|
||||
original: {},
|
||||
override: {},
|
||||
}
|
||||
|
|
@ -22,9 +26,21 @@ const defaultState: FlagState = {
|
|||
export default (state = defaultState, action: Actions): FlagState => {
|
||||
switch (action.type) {
|
||||
case SET_FEATURE_FLAGS:
|
||||
// just setting the loading state
|
||||
if (!action.payload.flags) {
|
||||
return {
|
||||
...state,
|
||||
status: action.payload.status,
|
||||
}
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
original: action.payload,
|
||||
status: action.payload.status,
|
||||
original: action.payload.flags,
|
||||
}
|
||||
case RESET_FEATURE_FLAGS:
|
||||
return {
|
||||
...defaultState,
|
||||
}
|
||||
case CLEAR_FEATURE_FLAG_OVERRIDES:
|
||||
return {
|
||||
|
|
|
|||
Loading…
Reference in New Issue