{filteredFlags.length > 0 ? (
- filteredFlags.map(({ feature_flag, value, hasOverride, hasVariants, currentValue }) => (
-
-
-
-
- {feature_flag.key}
-
-
+ filteredFlags.map(
+ ({ feature_flag, value, hasOverride, hasVariants, currentValue, payloadOverride }) => (
+
+
+
+
+ {feature_flag.key}
+
+
+
+
+
{
+ const newValue =
+ hasVariants && checked
+ ? (feature_flag.filters?.multivariate?.variants[0]
+ ?.key as string)
+ : checked
+ if (newValue === value && hasOverride) {
+ deleteOverriddenUserFlag(feature_flag.key)
+ } else {
+ setOverriddenUserFlag(feature_flag.key, newValue)
+ }
+ }}
+ />
-
{
- const newValue =
- hasVariants && checked
- ? (feature_flag.filters?.multivariate?.variants[0]?.key as string)
- : checked
- if (newValue === value && hasOverride) {
- deleteOverriddenUserFlag(feature_flag.key)
- } else {
- setOverriddenUserFlag(feature_flag.key, newValue)
- }
- }}
- />
-
+
+ <>
+ {hasVariants ? (
+ ({
+ label: `${variant.key} - ${variant.name} (${variant.rollout_percentage}%)`,
+ value: variant.key,
+ })) || []
+ }
+ onChange={(newValue) => {
+ if (newValue === value && hasOverride) {
+ deleteOverriddenUserFlag(feature_flag.key)
+ } else {
+ setOverriddenUserFlag(feature_flag.key, newValue)
+ }
+ }}
+ />
+ ) : null}
-
- ({
- label: `${variant.key} - ${variant.name} (${variant.rollout_percentage}%)`,
- value: variant.key,
- })) || []
- }
- onChange={(newValue) => {
- if (newValue === value && hasOverride) {
- deleteOverriddenUserFlag(feature_flag.key)
- } else {
- setOverriddenUserFlag(feature_flag.key, newValue)
- }
- }}
- />
-
-
- ))
+
+
+
+
+ debouncedSetDraftPayload(feature_flag.key, val)
+ }
+ placeholder='{"key": "value"}'
+ minRows={2}
+ />
+ savePayloadOverride(feature_flag.key)}
+ >
+ Save
+
+
+
+ >
+
+
+ )
+ )
) : (
{userFlagsLoading ? (
@@ -119,7 +169,9 @@ export const FlagsToolbarMenu = (): JSX.Element => {
- Note: overriding feature flags will only affect this browser.
+
+ Note: overriding feature flags and payloads will only affect this browser.
+
)
diff --git a/frontend/src/toolbar/flags/flagsToolbarLogic.ts b/frontend/src/toolbar/flags/flagsToolbarLogic.ts
index 07e7082646023..1c25de396eafd 100644
--- a/frontend/src/toolbar/flags/flagsToolbarLogic.ts
+++ b/frontend/src/toolbar/flags/flagsToolbarLogic.ts
@@ -11,6 +11,8 @@ import { CombinedFeatureFlagAndValueType } from '~/types'
import type { flagsToolbarLogicType } from './flagsToolbarLogicType'
+export type PayloadOverrides = Record
+
export const flagsToolbarLogic = kea([
path(['toolbar', 'flags', 'flagsToolbarLogic']),
connect(() => ({
@@ -22,11 +24,23 @@ export const flagsToolbarLogic = kea([
flags,
variants,
}),
- setOverriddenUserFlag: (flagKey: string, overrideValue: string | boolean) => ({ flagKey, overrideValue }),
+ setOverriddenUserFlag: (
+ flagKey: string,
+ overrideValue: string | boolean,
+ payloadOverride?: PayloadOverrides
+ ) => ({
+ flagKey,
+ overrideValue,
+ payloadOverride,
+ }),
+ setPayloadOverride: (flagKey: string, payload: any) => ({ flagKey, payload }),
deleteOverriddenUserFlag: (flagKey: string) => ({ flagKey }),
setSearchTerm: (searchTerm: string) => ({ searchTerm }),
checkLocalOverrides: true,
storeLocalOverrides: (localOverrides: Record) => ({ localOverrides }),
+ setDraftPayload: (flagKey: string, draftPayload: string) => ({ flagKey, draftPayload }),
+ savePayloadOverride: (flagKey: string) => ({ flagKey }),
+ setPayloadError: (flagKey: string, error: string | null) => ({ flagKey, error }),
}),
loaders(({ values }) => ({
userFlags: [
@@ -70,11 +84,52 @@ export const flagsToolbarLogic = kea([
},
},
],
+ payloadOverrides: [
+ {} as PayloadOverrides,
+ {
+ setPayloadOverride: (state, { flagKey, payload }) => ({
+ ...state,
+ [flagKey]: payload,
+ }),
+ deleteOverriddenUserFlag: (state, { flagKey }) => {
+ const newState = { ...state }
+ delete newState[flagKey]
+ return newState
+ },
+ },
+ ],
+ draftPayloads: [
+ {} as Record,
+ {
+ setDraftPayload: (state, { flagKey, draftPayload }) => ({
+ ...state,
+ [flagKey]: draftPayload,
+ }),
+ deleteOverriddenUserFlag: (state, { flagKey }) => {
+ const newState = { ...state }
+ delete newState[flagKey]
+ return newState
+ },
+ },
+ ],
+ payloadErrors: [
+ {} as Record,
+ {
+ setPayloadError: (state, { flagKey, error }) => ({
+ ...state,
+ [flagKey]: error,
+ }),
+ setDraftPayload: (state, { flagKey }) => ({
+ ...state,
+ [flagKey]: null,
+ }),
+ },
+ ],
}),
selectors({
userFlagsWithOverrideInfo: [
- (s) => [s.userFlags, s.localOverrides, s.posthogClientFlagValues],
- (userFlags, localOverrides, posthogClientFlagValues) => {
+ (s) => [s.userFlags, s.localOverrides, s.posthogClientFlagValues, s.payloadOverrides],
+ (userFlags, localOverrides, posthogClientFlagValues, payloadOverrides) => {
return userFlags.map((flag) => {
const hasVariants = (flag.feature_flag.filters?.multivariate?.variants?.length || 0) > 0
@@ -88,6 +143,7 @@ export const flagsToolbarLogic = kea([
hasVariants,
currentValue,
hasOverride: flag.feature_flag.key in localOverrides,
+ payloadOverride: payloadOverrides[flag.feature_flag.key],
}
})
},
@@ -115,12 +171,19 @@ export const flagsToolbarLogic = kea([
actions.storeLocalOverrides(locallyOverrideFeatureFlags)
}
},
- setOverriddenUserFlag: ({ flagKey, overrideValue }) => {
+ setOverriddenUserFlag: ({ flagKey, overrideValue, payloadOverride }) => {
const clientPostHog = values.posthog
if (clientPostHog) {
- clientPostHog.featureFlags.override({ ...values.localOverrides, [flagKey]: overrideValue })
+ const payloads = payloadOverride ? { [flagKey]: payloadOverride } : undefined
+ clientPostHog.featureFlags.overrideFeatureFlags({
+ flags: { ...values.localOverrides, [flagKey]: overrideValue },
+ payloads: payloads,
+ })
toolbarPosthogJS.capture('toolbar feature flag overridden')
actions.checkLocalOverrides()
+ if (payloadOverride) {
+ actions.setPayloadOverride(flagKey, payloadOverride)
+ }
clientPostHog.featureFlags.reloadFeatureFlags()
}
},
@@ -130,15 +193,26 @@ export const flagsToolbarLogic = kea([
const updatedFlags = { ...values.localOverrides }
delete updatedFlags[flagKey]
if (Object.keys(updatedFlags).length > 0) {
- clientPostHog.featureFlags.override({ ...updatedFlags })
+ clientPostHog.featureFlags.overrideFeatureFlags({ flags: updatedFlags })
} else {
- clientPostHog.featureFlags.override(false)
+ clientPostHog.featureFlags.overrideFeatureFlags(false)
}
toolbarPosthogJS.capture('toolbar feature flag override removed')
actions.checkLocalOverrides()
clientPostHog.featureFlags.reloadFeatureFlags()
}
},
+ savePayloadOverride: ({ flagKey }) => {
+ try {
+ const draftPayload = values.draftPayloads[flagKey]
+ const payload = draftPayload ? JSON.parse(draftPayload) : null
+ actions.setPayloadError(flagKey, null)
+ actions.setOverriddenUserFlag(flagKey, true, payload)
+ } catch (e) {
+ actions.setPayloadError(flagKey, 'Invalid JSON')
+ console.error('Invalid JSON:', e)
+ }
+ },
})),
permanentlyMount(),
])