Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix exportAll to export every experiments in DB #2144

Merged
merged 8 commits into from
Dec 11, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,38 @@ export class ExperimentController {
@Req() request: AppRequest
): Promise<Experiment[]> {
const experimentIds = params.ids;
return this.experimentService.exportExperiment(experimentIds, currentUser, request.logger);
return this.experimentService.exportExperiment(currentUser, request.logger, experimentIds);
}

/**
* @swagger
* /experiments/all:
* get:
* description: Export All Experiment JSON
* tags:
* - Experiments
* produces:
* - application/json
* responses:
* '200':
* description: Experiments are exported
* schema:
* type: array
* items:
* type: object
* properties:
* fileName:
* type: string
* error:
* type: string
* '401':
* description: AuthorizationRequiredError
* '500':
* description: Internal Server Error
*/
@Get('/export/all')
public exportAllExperiment(@CurrentUser() currentUser: UserDTO, @Req() request: AppRequest): Promise<Experiment[]> {
return this.experimentService.exportExperiment(currentUser, request.logger);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,10 +566,10 @@ export class ExperimentService {
return validatedExperiments;
}

public async exportExperiment(experimentIds: string[], user: UserDTO, logger: UpgradeLogger): Promise<Experiment[]> {
public async exportExperiment(user: UserDTO, logger: UpgradeLogger, experimentIds?: string[]): Promise<Experiment[]> {
logger.info({ message: `Inside export Experiment JSON ${experimentIds}` });
const experimentDetails = await this.experimentRepository.find({
where: { id: In(experimentIds) },
where: experimentIds ? { id: In(experimentIds) } : undefined,
relations: [
'partitions',
'conditions',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ export class ExperimentDataService {
return this.http.get(url, { params: ids });
}

exportAllExperimentDesign() {
const url = `${this.environment.api.exportAllExperiment}`;
return this.http.get(url);
}

fetchExperimentGraphInfo(params: any) {
const url = this.environment.api.experimentGraphInfo;
return this.http.post(url, params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,12 @@ describe('ExperimentService', () => {

service.exportExperimentDesign([experimentId]);

expect(mockStore.dispatch).toHaveBeenCalledWith(actionExportExperimentDesign({ experimentIds: [experimentId] }));
expect(mockStore.dispatch).toHaveBeenCalledWith(
actionExportExperimentDesign({
experimentIds: [experimentId],
exportAll: false,
})
);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
selectIsPollingExperimentDetailStats,
selectCurrentContextMetaDataConditions,
selectIsLoadingContextMetaData,
selectExperimentsExportLoading,
} from './store/experiments.selectors';
import * as experimentAction from './store//experiments.actions';
import { AppState } from '../core.state';
Expand Down Expand Up @@ -63,6 +64,7 @@ export class ExperimentService {
isLoadingExperiment$ = this.store$.pipe(select(selectIsLoadingExperiment));
isLoadingExperimentDetailStats$ = this.store$.pipe(select(selectIsLoadingExperimentDetailStats));
isPollingExperimentDetailStats$ = this.store$.pipe(select(selectIsPollingExperimentDetailStats));
isExperimentsExportLoading$ = this.store$.pipe(select(selectExperimentsExportLoading));
selectedExperiment$ = this.store$.pipe(select(selectSelectedExperiment));
allDecisionPoints$ = this.store$.pipe(select(selectAllDecisionPoints));
allExperimentNames$ = this.store$.pipe(select(selectAllExperimentNames));
Expand Down Expand Up @@ -204,7 +206,11 @@ export class ExperimentService {
}

exportExperimentDesign(experimentIds: string[]) {
this.store$.dispatch(experimentAction.actionExportExperimentDesign({ experimentIds }));
this.store$.dispatch(experimentAction.actionExportExperimentDesign({ experimentIds, exportAll: false }));
}

exportAllExperimentDesign() {
this.store$.dispatch(experimentAction.actionExportExperimentDesign({ experimentIds: [], exportAll: true }));
}

importExperiment(experiments: Experiment[]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export const actionExportExperimentInfo = createAction(

export const actionExportExperimentDesign = createAction(
'[Experiment] Export Experiment Design',
props<{ experimentIds: string[] }>()
props<{ experimentIds: string[]; exportAll: boolean }>()
);

export const actionExportExperimentInfoSuccess = createAction('[Experiment] Export Experiment Info Success');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,12 @@ describe('ExperimentEffects', () => {
expect(resultingAction).toEqual(expectedAction);
});

actions$.next(actionExportExperimentDesign({ experimentIds: [experimentId] }));
actions$.next(
actionExportExperimentDesign({
experimentIds: [experimentId],
exportAll: false,
})
);

tick(0);
}));
Expand All @@ -1367,7 +1372,12 @@ describe('ExperimentEffects', () => {
expect(resultingAction).toEqual(expectedAction);
});

actions$.next(actionExportExperimentDesign({ experimentIds: [experimentId] }));
actions$.next(
actionExportExperimentDesign({
experimentIds: [experimentId],
exportAll: false,
})
);

tick(0);
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,10 +463,13 @@ export class ExperimentEffects {
exportExperimentDesign$ = createEffect(() =>
this.actions$.pipe(
ofType(experimentAction.actionExportExperimentDesign),
map((action) => ({ experimentIds: action.experimentIds })),
map((action) => ({ experimentIds: action.experimentIds, exportAll: action.exportAll })),
filter(({ experimentIds }) => !!experimentIds),
switchMap(({ experimentIds }) =>
this.experimentDataService.exportExperimentDesign(experimentIds).pipe(
switchMap(({ experimentIds, exportAll: allExport }) => {
const apiCall$ = allExport
? this.experimentDataService.exportAllExperimentDesign()
: this.experimentDataService.exportExperimentDesign(experimentIds);
return apiCall$.pipe(
tap(() => {
this.notificationService.showSuccess('Experiment Design JSON downloaded!');
}),
Expand All @@ -485,8 +488,8 @@ export class ExperimentEffects {
return experimentAction.actionExportExperimentDesignSuccess();
}),
catchError(() => [experimentAction.actionExportExperimentDesignFailure()])
)
)
);
})
)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ export interface ExperimentState extends EntityState<Experiment> {
isLoadingExperiment: boolean;
isLoadingExperimentDetailStats: boolean;
isPollingExperimentDetailStats: boolean;
isLoadingExperimentExport: boolean;
skipExperiment: number;
totalExperiments: number;
searchKey: EXPERIMENT_SEARCH_KEY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const initialState: ExperimentState = adapter.getInitialState({
isLoadingExperiment: false,
isLoadingExperimentDetailStats: false,
isPollingExperimentDetailStats: false,
isLoadingExperimentExport: false,
skipExperiment: 0,
totalExperiments: null,
searchKey: EXPERIMENT_SEARCH_KEY.ALL,
Expand Down Expand Up @@ -152,6 +153,14 @@ const reducer = createReducer(
on(experimentsAction.actionEndExperimentDetailStatsPolling, (state) => ({
...state,
isPollingExperimentDetailStats: false,
})),
on(experimentsAction.actionExportExperimentDesign, (state) => ({
...state,
isLoadingExperimentExport: true,
})),
on(experimentsAction.actionExportExperimentDesignSuccess, (state) => ({
...state,
isLoadingExperimentExport: false,
}))
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export const selectIsGraphLoading = createSelector(selectExperimentState, (state

export const selectExperimentGraphRange = createSelector(selectExperimentState, (state) => state.graphRange);

export const selectExperimentsExportLoading = createSelector(
selectExperimentState,
(state) => state.isLoadingExperimentExport
);

export const selectExperimentGraphInfo = createSelector(
selectExperimentState,
selectExperimentGraphRange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('LocalStorageService', () => {
isLoadingExperiment: false,
isLoadingExperimentDetailStats: false,
isPollingExperimentDetailStats: false,
isLoadingExperimentExport: false,
skipExperiment: 0,
totalExperiments: null,
searchKey: EXPERIMENT_SEARCH_KEY.ALL,
Expand All @@ -50,6 +51,7 @@ describe('LocalStorageService', () => {
isLoadingExperiment: false,
isLoadingExperimentDetailStats: false,
isPollingExperimentDetailStats: false,
isLoadingExperimentExport: false,
skipExperiment: 0,
totalExperiments: null,
searchKey: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class LocalStorageService {
isLoadingExperiment: false,
isLoadingExperimentDetailStats: false,
isPollingExperimentDetailStats: false,
isLoadingExperimentExport: false,
skipExperiment: 0,
totalExperiments: null,
searchKey: experimentSearchKey as EXPERIMENT_SEARCH_KEY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export class ExperimentListComponent implements OnInit, OnDestroy, AfterViewInit
openExportAllExperimentsDialog() {
this.dialog.open(ExportModalComponent, {
panelClass: 'export-modal',
data: { experiment: this.allExperiments.data },
data: { experiment: this.allExperiments.data, exportAll: true },
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,53 @@
<div class="export-experiment-container">
<span class="ft-24-700 title">{{ 'home.view-experiment.export-experiment.text' | translate | titlecase }}</span>

<form [formGroup]="exportForm" class="export-experiment-container-form">
<mat-form-field class="dense-2" appearance="outline" subscriptSizing="dynamic">
<mat-label class="ft-14-400">
{{ 'home.view-experiment.export-method.text' | translate }}
</mat-label>
<mat-select class="ft-14-400 export-selection" formControlName="exportMethod">
<mat-option class="ft-14-400" *ngFor="let method of exportMethod" [value]="method.value">
{{ method.value }}
</mat-option>
</mat-select>
</mat-form-field>
</form>
<span
class="ft-14-400 info-msg"
*ngIf="exportForm.get('exportMethod').value === 'Email Experiment Data (CSV)'"
[innerHTML]="'home.view-experiment.export-warning.text' | translate: { email: emailId }"
></span>
<span
class="ft-14-400 info-msg"
*ngIf="exportForm.get('exportMethod').value === 'Email Experiment Data (CSV)' && experiments.length > 1"
>
{{ 'home.view-experiment.export-csv-warning.text' | translate }}
</span>
<div class="button-container">
<button class="shared-modal--modal-btn" mat-raised-button (click)="onCancelClick()">
{{ 'global.cancel.text' | translate }}
</button>
<button
mat-raised-button
[ngClass]="{
'default-button--disabled':
<ng-container *ngIf="!(isExperimentsExportLoading$ | async); else loadingSpinner">
<form [formGroup]="exportForm" class="export-experiment-container-form">
<mat-form-field class="dense-2" appearance="outline" subscriptSizing="dynamic">
<mat-label class="ft-14-400">
{{ 'home.view-experiment.export-method.text' | translate }}
</mat-label>
<mat-select class="ft-14-400 export-selection" formControlName="exportMethod">
<mat-option class="ft-14-400" *ngFor="let method of exportMethod" [value]="method.value">
{{ method.value }}
</mat-option>
</mat-select>
</mat-form-field>
</form>
<span
class="ft-14-400 info-msg"
*ngIf="exportForm.get('exportMethod').value === 'Email Experiment Data (CSV)'"
[innerHTML]="'home.view-experiment.export-warning.text' | translate: { email: emailId }"
></span>
<span
class="ft-14-400 info-msg"
*ngIf="exportForm.get('exportMethod').value === 'Email Experiment Data (CSV)' && experiments.length > 1"
>
{{ 'home.view-experiment.export-csv-warning.text' | translate }}
</span>
<div class="button-container">
<button class="shared-modal--modal-btn" mat-raised-button (click)="onCancelClick()">
{{ 'global.cancel.text' | translate }}
</button>
<button
mat-raised-button
[ngClass]="{
'default-button--disabled':
(exportForm.get('exportMethod').value === 'Email Experiment Data (CSV)' && experiments.length > 1) ||
!isExportMethodSelected
}"
[disabled]="
(exportForm.get('exportMethod').value === 'Email Experiment Data (CSV)' && experiments.length > 1) ||
!isExportMethodSelected
}"
[disabled]="
(exportForm.get('exportMethod').value === 'Email Experiment Data (CSV)' && experiments.length > 1) ||
!isExportMethodSelected
"
class="shared-modal--modal-btn default-button"
(click)="exportExperiment()"
>
{{ 'home.view-experiment.export-button.text' | translate }}
</button>
</div>
"
class="shared-modal--modal-btn default-button"
(click)="exportExperiment()"
>
{{ 'home.view-experiment.export-button.text' | translate }}
</button>
</div>
</ng-container>
<ng-template #loadingSpinner>
<mat-spinner class="spinner" diameter="60"></mat-spinner>
</ng-template>
RidhamShah marked this conversation as resolved.
Show resolved Hide resolved
</div>
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
.export-experiment-container {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 250px;

.spinner {
width: 100%;
margin: auto;
}

RidhamShah marked this conversation as resolved.
Show resolved Hide resolved
.title {
display: block;
padding: 40px 40px 0;
Expand Down
Loading
Loading