From 1a33a9818481550b34b6c4da4ac9e4b3bfe89676 Mon Sep 17 00:00:00 2001 From: Phil Haack Date: Fri, 28 Feb 2025 17:52:44 -0800 Subject: [PATCH] Add button to duplicate feature flag Rather than actually duplicating the feature flag, it navigates to the new feature flag page and pre-populates the fields with the values from the source flag. This way, the user is required to pick a unique feature flag key. Fixes #21788 --- .../src/scenes/feature-flags/FeatureFlag.tsx | 7 +++++ .../src/scenes/feature-flags/FeatureFlags.tsx | 4 +++ .../scenes/feature-flags/featureFlagLogic.ts | 30 +++++++++++++++++++ frontend/src/scenes/urls.ts | 1 + 4 files changed, 42 insertions(+) diff --git a/frontend/src/scenes/feature-flags/FeatureFlag.tsx b/frontend/src/scenes/feature-flags/FeatureFlag.tsx index bb569cae82789..8eb162417e5f1 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlag.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlag.tsx @@ -546,6 +546,13 @@ export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element { )} + + Duplicate feature flag + + )} + + Duplicate feature flag + + Try out in Insights diff --git a/frontend/src/scenes/feature-flags/featureFlagLogic.ts b/frontend/src/scenes/feature-flags/featureFlagLogic.ts index f6febfbf8dd31..7f7e68fdcc952 100644 --- a/frontend/src/scenes/feature-flags/featureFlagLogic.ts +++ b/frontend/src/scenes/feature-flags/featureFlagLogic.ts @@ -574,6 +574,31 @@ export const featureFlagLogic = kea([ loaders(({ values, props, actions }) => ({ featureFlag: { loadFeatureFlag: async () => { + const sourceId = router.values.searchParams.sourceId + if (props.id === 'new' && sourceId) { + // Used when "duplicating a feature flag". This populates the form with the source flag's data. + const sourceFlag = await api.featureFlags.get(sourceId) + const { + id, + created_at, + created_by, + is_simple_flag, + experiment_set, + features, + surveys, + ...flagToKeep + } = sourceFlag + + // Remove sourceId from URL + router.actions.replace(router.values.location.pathname) + + return { + ...NEW_FLAG, + ...flagToKeep, + key: '', + } as FeatureFlagType + } + if (props.id && props.id !== 'new' && props.id !== 'link') { try { const retrievedFlag: FeatureFlagType = await api.featureFlags.get(props.id) @@ -1205,6 +1230,11 @@ export const featureFlagLogic = kea([ }, })), afterMount(({ props, actions }) => { + if (props.id === 'new' && router.values.searchParams.sourceId) { + actions.loadFeatureFlag() + return + } + const foundFlag = featureFlagsLogic .findMounted() ?.values.featureFlags.results.find((flag) => flag.id === props.id) diff --git a/frontend/src/scenes/urls.ts b/frontend/src/scenes/urls.ts index 6c65829783b0c..7bd391cfccb0f 100644 --- a/frontend/src/scenes/urls.ts +++ b/frontend/src/scenes/urls.ts @@ -178,6 +178,7 @@ export const urls = { experimentsSharedMetric: (id: string | number): string => `/experiments/shared-metrics/${id}`, featureFlags: (tab?: string): string => `/feature_flags${tab ? `?tab=${tab}` : ''}`, featureFlag: (id: string | number): string => `/feature_flags/${id}`, + featureFlagDuplicate: (sourceId: number | string | null): string => `/feature_flags/new?sourceId=${sourceId}`, featureManagement: (id?: string | number): string => `/features${id ? `/${id}` : ''}`, errorTracking: (): string => '/error_tracking', errorTrackingConfiguration: (): string => '/error_tracking/configuration',