diff --git a/frontend/src/component/feature/FeatureStrategy/NewFeatureStrategyEdit/NewFeatureStrategyEdit.tsx b/frontend/src/component/feature/FeatureStrategy/NewFeatureStrategyEdit/NewFeatureStrategyEdit.tsx index 09fc2e9640..5f5bceb51c 100644 --- a/frontend/src/component/feature/FeatureStrategy/NewFeatureStrategyEdit/NewFeatureStrategyEdit.tsx +++ b/frontend/src/component/feature/FeatureStrategy/NewFeatureStrategyEdit/NewFeatureStrategyEdit.tsx @@ -30,6 +30,11 @@ import { NewFeatureStrategyForm } from 'component/feature/FeatureStrategy/Featur import { NewStrategyVariants } from 'component/feature/StrategyTypes/NewStrategyVariants'; import { constraintId } from 'component/common/ConstraintAccordion/ConstraintAccordionList/createEmptyConstraint'; import { v4 as uuidv4 } from 'uuid'; +import { useScheduledChangeRequestsWithStrategy } from 'hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy'; +import { + getChangeRequestConflictCreatedData, + getChangeRequestConflictCreatedDataFromScheduleData, +} from './change-request-conflict-data'; const useTitleTracking = () => { const [previousTitle, setPreviousTitle] = useState(''); @@ -103,7 +108,7 @@ export const NewFeatureStrategyEdit = () => { const navigate = useNavigate(); const { addChange } = useChangeRequestApi(); const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId); - const { refetch: refetchChangeRequests } = + const { refetch: refetchChangeRequests, data: pendingChangeRequests } = usePendingChangeRequests(projectId); const { setPreviousTitle } = useTitleTracking(); @@ -134,6 +139,37 @@ export const NewFeatureStrategyEdit = () => { } }, [feature]); + const { trackEvent } = usePlausibleTracker(); + const { changeRequests: scheduledChangeRequestThatUseStrategy } = + useScheduledChangeRequestsWithStrategy(projectId, strategyId); + + const pendingCrsUsingThisStrategy = getChangeRequestConflictCreatedData( + pendingChangeRequests, + featureId, + strategyId, + uiConfig, + ); + + const scheduledCrsUsingThisStrategy = + getChangeRequestConflictCreatedDataFromScheduleData( + scheduledChangeRequestThatUseStrategy, + uiConfig, + ); + + const emitConflictsCreatedEvents = (): void => + [ + ...pendingCrsUsingThisStrategy, + ...scheduledCrsUsingThisStrategy, + ].forEach((data) => + trackEvent('change_request', { + props: { + ...data, + action: 'edit-strategy', + eventType: 'conflict-created', + }, + }), + ); + const { segments: savedStrategySegments, refetchSegments: refetchSavedStrategySegments, @@ -201,6 +237,7 @@ export const NewFeatureStrategyEdit = () => { } else { await onStrategyEdit(payload); } + emitConflictsCreatedEvents(); refetchFeature(); navigate(formatFeaturePath(projectId, featureId)); } catch (error: unknown) { diff --git a/frontend/src/component/feature/FeatureStrategy/NewFeatureStrategyEdit/change-request-conflict-data.test.ts b/frontend/src/component/feature/FeatureStrategy/NewFeatureStrategyEdit/change-request-conflict-data.test.ts new file mode 100644 index 0000000000..262b20bf4d --- /dev/null +++ b/frontend/src/component/feature/FeatureStrategy/NewFeatureStrategyEdit/change-request-conflict-data.test.ts @@ -0,0 +1,113 @@ +import { IUiConfig } from 'interfaces/uiConfig'; +import { + getChangeRequestConflictCreatedData, + getChangeRequestConflictCreatedDataFromScheduleData, +} from './change-request-conflict-data'; + +const uiConfig: Pick = { + baseUriPath: '/some-base-uri', +}; +const unleashIdentifier = uiConfig.baseUriPath; +const featureId = 'flag-with-deleted-scheduler'; +const strategyId = 'ed2ffa14-004c-4ed1-931b-78761681c54a'; + +const changeRequestWithStrategy = { + id: 105, + features: [ + { + name: featureId, + changes: [ + { + action: 'updateStrategy' as const, + payload: { + id: strategyId, + }, + }, + ], + }, + ], + state: 'In review' as const, +}; + +const changeRequestWithoutStrategy = { + id: 106, + features: [ + { + name: featureId, + changes: [ + { + action: 'deleteStrategy' as const, + payload: { + id: strategyId, + }, + }, + ], + }, + { + name: featureId, + changes: [ + { + action: 'addStrategy' as const, + payload: {}, + }, + ], + }, + ], + state: 'In review' as const, +}; + +test('it finds crs that update a strategy', () => { + const results = getChangeRequestConflictCreatedData( + [changeRequestWithStrategy], + featureId, + strategyId, + uiConfig, + ); + + expect(results).toStrictEqual([ + { + state: changeRequestWithStrategy.state, + changeRequest: `${unleashIdentifier}#${changeRequestWithStrategy.id}`, + }, + ]); +}); + +test('it does not return crs that do not update a strategy', () => { + const results = getChangeRequestConflictCreatedData( + [changeRequestWithoutStrategy], + featureId, + strategyId, + uiConfig, + ); + + expect(results).toStrictEqual([]); +}); + +test('it maps scheduled change request data', () => { + const scheduledChanges = [ + { + id: 103, + environment: 'development', + }, + { + id: 104, + environment: 'development', + }, + ]; + + const results = getChangeRequestConflictCreatedDataFromScheduleData( + scheduledChanges, + uiConfig, + ); + + expect(results).toStrictEqual([ + { + state: 'Scheduled', + changeRequest: `${unleashIdentifier}#103`, + }, + { + state: 'Scheduled', + changeRequest: `${unleashIdentifier}#104`, + }, + ]); +}); diff --git a/frontend/src/component/feature/FeatureStrategy/NewFeatureStrategyEdit/change-request-conflict-data.ts b/frontend/src/component/feature/FeatureStrategy/NewFeatureStrategyEdit/change-request-conflict-data.ts new file mode 100644 index 0000000000..8df88432e7 --- /dev/null +++ b/frontend/src/component/feature/FeatureStrategy/NewFeatureStrategyEdit/change-request-conflict-data.ts @@ -0,0 +1,55 @@ +import { + ChangeRequestState, + ChangeRequestType, + IChangeRequestFeature, + IFeatureChange, +} from 'component/changeRequest/changeRequest.types'; +import { ScheduledChangeRequestViewModel } from 'hooks/api/getters/useScheduledChangeRequestsWithStrategy/useScheduledChangeRequestsWithStrategy'; +import { IUiConfig } from 'interfaces/uiConfig'; +import { getUniqueChangeRequestId } from 'utils/unique-change-request-id'; + +type ChangeRequestConflictCreatedData = { + changeRequest: string; + state: ChangeRequestState; +}; + +export const getChangeRequestConflictCreatedData = ( + changeRequests: + | { + state: ChangeRequestType['state']; + id: ChangeRequestType['id']; + features: { + name: IChangeRequestFeature['name']; + changes: (Pick & { + payload: { id?: number | string }; + })[]; + }[]; + }[] + | undefined, + featureId: string, + strategyId: string, + uiConfig: Pick, +): ChangeRequestConflictCreatedData[] => + changeRequests + ?.filter((cr) => + cr.features + .find((feature) => feature.name === featureId) + ?.changes.some( + (change) => + change.action === 'updateStrategy' && + change.payload.id === strategyId, + ), + ) + .map((cr) => ({ + changeRequest: getUniqueChangeRequestId(uiConfig, cr.id), + state: cr.state, + })) ?? []; + +export const getChangeRequestConflictCreatedDataFromScheduleData = ( + changeRequests: Pick[] | undefined, + uiConfig: Pick, +): ChangeRequestConflictCreatedData[] => + changeRequests?.map((cr) => ({ + changeRequest: getUniqueChangeRequestId(uiConfig, cr.id), + state: 'Scheduled' as const, + })) ?? [];