Skip to content

Commit

Permalink
Feature Flag export JSON and CSV modals with crud changes (#1759)
Browse files Browse the repository at this point in the history
* Feature Flag export JSON and CSV modals with crud changes on frontend side

* lint fix for error?

* addressed the review comments

* addressed the review comments

* bug fix for root page

* resolve merge conflicts and review cmts

* resolve merge conflicts errors

* addressed the review comments

* resolved the review comments

* use fetch featureflagByID api to export design

---------

Co-authored-by: danoswaltCL <[email protected]>
Co-authored-by: danoswaltCL <[email protected]>
  • Loading branch information
3 people authored Aug 6, 2024
1 parent cd2e80f commit 2d68920
Show file tree
Hide file tree
Showing 20 changed files with 301 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export const selectIsAuthenticating = createSelector(selectAuthState, (state: Au

export const selectCurrentUser = createSelector(selectAuthState, (state: AuthState) => state.user);

export const selectCurrentUserEmail = createSelector(
selectAuthState,
(state) => state.user?.email || ''
);

export const selectRedirectUrl = createSelector(selectAuthState, (state: AuthState) =>
state.redirectUrl ? state.redirectUrl : null
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Inject, Injectable } from '@angular/core';
import { ENV, Environment } from '../../../environments/environment-types';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import {
AddFeatureFlagRequest,
FeatureFlag,
Expand All @@ -10,11 +10,12 @@ import {
UpdateFeatureFlagRequest,
UpdateFeatureFlagStatusRequest,
} from './store/feature-flags.model';
import { Observable } from 'rxjs';
import { Observable, delay, of } from 'rxjs';
import { AddPrivateSegmentListRequest, EditPrivateSegmentListRequest } from '../segments/store/segments.model';

@Injectable()
export class FeatureFlagsDataService {
mockFeatureFlags: FeatureFlag[] = [];
constructor(private http: HttpClient, @Inject(ENV) private environment: Environment) {}

fetchFeatureFlagsPaginated(params: FeatureFlagsPaginationParams): Observable<FeatureFlagsPaginationInfo> {
Expand Down Expand Up @@ -42,6 +43,22 @@ export class FeatureFlagsDataService {
return this.http.put<FeatureFlag>(url, flag);
}

emailFeatureFlagData(flagId: string, email: string){
let featureFlagInfoParams = new HttpParams();
featureFlagInfoParams = featureFlagInfoParams.append('experimentId', flagId);
featureFlagInfoParams = featureFlagInfoParams.append('email', email);

const url = this.environment.api.emailFlagData;
// return this.http.post(url, { params: featureFlagInfoParams });

// mock
return of(true).pipe(delay(2000));
}

exportFeatureFlagsDesign(flagId: string) {
return this.fetchFeatureFlagById(flagId);
}

deleteFeatureFlag(id: string) {
const url = `${this.environment.api.featureFlag}/${id}`;
return this.http.delete(url);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
selectSortKey,
selectSortAs,
selectAppContexts,
selectFeatureFlagIds,
} from './store/feature-flags.selectors';
import * as FeatureFlagsActions from './store/feature-flags.actions';
import { actionFetchContextMetaData } from '../experiments/store/experiments.actions';
Expand All @@ -33,15 +34,20 @@ import {
} from './store/feature-flags.model';
import { filter, map, pairwise } from 'rxjs';
import isEqual from 'lodash.isequal';
import { selectCurrentUserEmail } from '../auth/store/auth.selectors';
import { AddPrivateSegmentListRequest, EditPrivateSegmentListRequest } from '../segments/store/segments.model';

@Injectable()
export class FeatureFlagsService {
constructor(private store$: Store<AppState>) {}

currentUserEmailAddress$ = this.store$.pipe(select(selectCurrentUserEmail));
allFeatureFlagsIds$ = this.store$.pipe(select(selectFeatureFlagIds));
isInitialFeatureFlagsLoading$ = this.store$.pipe(select(selectHasInitialFeatureFlagsDataLoaded));
isLoadingFeatureFlags$ = this.store$.pipe(select(selectIsLoadingFeatureFlags));
isLoadingSelectedFeatureFlag$ = this.store$.pipe(select(selectIsLoadingSelectedFeatureFlag));
isLoadingUpsertFeatureFlag$ = this.store$.pipe(select(selectIsLoadingUpsertFeatureFlag));
IsLoadingFeatureFlagDelete$ = this.store$.pipe(select(selectIsLoadingFeatureFlagDelete));
isLoadingUpdateFeatureFlagStatus$ = this.store$.pipe(select(selectIsLoadingUpdateFeatureFlagStatus));
isLoadingUpsertPrivateSegmentList$ = this.store$.pipe(select(selectIsLoadingUpsertFeatureFlag));
allFeatureFlags$ = this.store$.pipe(select(selectAllFeatureFlagsSortedByDate));
Expand All @@ -51,8 +57,6 @@ export class FeatureFlagsService {
searchKey$ = this.store$.pipe(select(selectSearchKey));
sortKey$ = this.store$.pipe(select(selectSortKey));
sortAs$ = this.store$.pipe(select(selectSortAs));
isLoadingUpsertFeatureFlag$ = this.store$.pipe(select(selectIsLoadingUpsertFeatureFlag));
IsLoadingFeatureFlagDelete$ = this.store$.pipe(select(selectIsLoadingFeatureFlagDelete));

hasFeatureFlagsCountChanged$ = this.allFeatureFlags$.pipe(
pairwise(),
Expand Down Expand Up @@ -124,6 +128,14 @@ export class FeatureFlagsService {
this.store$.dispatch(FeatureFlagsActions.actionDeleteFeatureFlag({ flagId }));
}

emailFeatureFlagData(featureFlagId: string) {
this.store$.dispatch(FeatureFlagsActions.actionEmailFeatureFlagData({ featureFlagId }));
}

exportFeatureFlagsData(featureFlagId: string) {
this.store$.dispatch(FeatureFlagsActions.actionExportFeatureFlagDesign({ featureFlagId }));
}

setSearchKey(searchKey: FLAG_SEARCH_KEY) {
this.store$.dispatch(FeatureFlagsActions.actionSetSearchKey({ searchKey }));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,24 @@ export const actionUpdateFeatureFlagSuccess = createAction(

export const actionUpdateFeatureFlagFailure = createAction('[Feature Flags] Update Feature Flag Failure');

export const actionEmailFeatureFlagData = createAction(
'[Feature Flags] Email Feature Flag Data',
props<{ featureFlagId: string }>()
);

export const actionEmailFeatureFlagDataSuccess = createAction('[Feature Flags] Email Feature Flag Data Success');

export const actionEmailFeatureFlagDataFailure = createAction('[Feature Flags] Email Feature Flag Data Failure');

export const actionExportFeatureFlagDesign = createAction(
'[Feature Flags] Export Feature Flag Design',
props<{ featureFlagId: string }>()
);

export const actionExportFeatureFlagDesignSuccess = createAction('[Feature Flags] Export Feature Flag Design Success');

export const actionExportFeatureFlagDesignFailure = createAction('[Feature Flags] Export Feature Flag Design Failure');

export const actionSetIsLoadingFeatureFlags = createAction(
'[Feature Flags] Set Is Loading Flags',
props<{ isLoadingFeatureFlags: boolean }>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { catchError, switchMap, map, filter, withLatestFrom, tap, first } from '
import { FeatureFlag, FeatureFlagsPaginationParams, NUMBER_OF_FLAGS } from './feature-flags.model';
import { Router } from '@angular/router';
import { Store, select } from '@ngrx/store';
import { AppState } from '../../core.module';
import { AppState, NotificationService } from '../../core.module';
import {
selectTotalFlags,
selectSearchKey,
Expand All @@ -15,7 +15,10 @@ import {
selectSortAs,
selectSearchString,
selectIsAllFlagsFetched,
selectSelectedFeatureFlag,
} from './feature-flags.selectors';
import { selectCurrentUser } from '../../auth/store/auth.selectors';
import { CommonExportHelpersService } from '../../../shared/services/common-export-helpers.service';
import { of } from 'rxjs';

@Injectable()
Expand All @@ -24,7 +27,9 @@ export class FeatureFlagsEffects {
private store$: Store<AppState>,
private actions$: Actions,
private featureFlagsDataService: FeatureFlagsDataService,
private router: Router
private router: Router,
private notificationService: NotificationService,
private commonExportHelpersService: CommonExportHelpersService,
) {}

fetchFeatureFlags$ = createEffect(() =>
Expand Down Expand Up @@ -149,6 +154,25 @@ export class FeatureFlagsEffects {
)
);


upsertFeatureFlagInclusionList$ = createEffect(() =>
this.actions$.pipe(
ofType(FeatureFlagsActions.actionAddFeatureFlagInclusionList),
map((action) => action.list),
withLatestFrom(this.store$.pipe(select(selectSelectedFeatureFlag))),
switchMap(([list, flag]) => {
const request = {
flagId: flag.id,
...list,
};
return this.featureFlagsDataService.addInclusionList(request).pipe(
map((listResponse) => FeatureFlagsActions.actionUpdateFeatureFlagInclusionListSuccess({ listResponse })),
catchError((error) => of(FeatureFlagsActions.actionUpdateFeatureFlagInclusionListFailure({ error })))
);
})
)
);

addFeatureFlagInclusionList$ = createEffect(() =>
this.actions$.pipe(
ofType(FeatureFlagsActions.actionAddFeatureFlagInclusionList),
Expand Down Expand Up @@ -231,5 +255,45 @@ export class FeatureFlagsEffects {
)
);

emailFeatureFlagData$ = createEffect(() =>
this.actions$.pipe(
ofType(FeatureFlagsActions.actionEmailFeatureFlagData),
map((action) => ({ featureFlagId: action.featureFlagId })),
withLatestFrom(this.store$.pipe(select(selectCurrentUser))),
filter(([{ featureFlagId }, { email }]) => !!featureFlagId && !!email),
switchMap(([{ featureFlagId }, { email }]) =>
this.featureFlagsDataService.emailFeatureFlagData(featureFlagId, email).pipe(
map(() => {
this.notificationService.showSuccess(`Email will be sent to ${email}`);
return FeatureFlagsActions.actionEmailFeatureFlagDataSuccess();
}),
catchError(() => [FeatureFlagsActions.actionEmailFeatureFlagDataFailure()])
)
)
)
);
exportFeatureFlagsDesign$ = createEffect(() =>
this.actions$.pipe(
ofType(FeatureFlagsActions.actionExportFeatureFlagDesign),
map((action) => ({ featureFlagId: action.featureFlagId })),
filter(({ featureFlagId }) => !!featureFlagId),
switchMap(({ featureFlagId }) =>
this.featureFlagsDataService.exportFeatureFlagsDesign(featureFlagId).pipe(
map((data) => {
if (data) {
this.commonExportHelpersService.convertDataToDownload([data], 'FeatureFlags');
this.notificationService.showSuccess('Feature Flag Design JSON downloaded!');
}
return FeatureFlagsActions.actionExportFeatureFlagDesignSuccess();
}),
catchError((error) => {
this.notificationService.showError('Failed to export Feature Flag Design');
return of(FeatureFlagsActions.actionExportFeatureFlagDesignFailure());
})
)
)
)
);

private getSearchString$ = () => this.store$.pipe(select(selectSearchString)).pipe(first());
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ export interface FeatureFlagsPaginationParams {
sortParams?: IFeatureFlagsSortParams;
}

export enum FEATURE_FLAG_DETAILS_PAGE_ACTIONS {
EDIT = 'Edit Feature Flag',
DUPLICATE = 'Duplicate Feature Flag',
ARCHIVE = 'Archive Feature Flag',
DELETE = 'Delete Feature Flag',
EXPORT_DESIGN = 'Export Feature Flag Design',
EMAIL_DATA = 'Email Feature Flag Data'
}

export enum FEATURE_FLAG_PARTICIPANT_LIST_KEY {
INCLUDE = 'featureFlagSegmentInclusion',
EXCLUDE = 'featureFlagSegmentExclusion',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { createSelector, createFeatureSelector } from '@ngrx/store';
import { FLAG_SEARCH_KEY, FeatureFlag, FeatureFlagState, ParticipantListTableRow } from './feature-flags.model';
import { selectRouterState } from '../../core.state';
import { selectAll } from './feature-flags.reducer';
import { selectContextMetaData } from '../../experiments/store/experiments.selectors';
import { selectAll, selectIds } from './feature-flags.reducer';

export const selectFeatureFlagsState = createFeatureSelector<FeatureFlagState>('featureFlags');

export const selectAllFeatureFlags = createSelector(selectFeatureFlagsState, selectAll);

export const selectFeatureFlagIds = createSelector(selectFeatureFlagsState, selectIds);

export const selectAllFeatureFlagsSortedByDate = createSelector(selectAllFeatureFlags, (featureFlags) => {
if (!featureFlags) {
return [];
Expand Down
Loading

0 comments on commit 2d68920

Please sign in to comment.