NIFI-12767: Error handling in Provenance and Lineage (#8386)

* NIFI-12767:
- Error handling in Provenance and Lineage.

* NIFI-12767:
- Addressing review feedback.

* NIFI-12767:
- Restoring empty initial state for completed request that allows showing a banner error over an empty table instead of an empty page.

* NIFI-12767:
- Restoring error state to ensure that the loading spinner stops on error.

This closes #8386
This commit is contained in:
Matt Gilman 2024-02-14 11:11:26 -05:00 committed by GitHub
parent 4f030fefca
commit da8f86b7e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
65 changed files with 465 additions and 321 deletions

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AddTenantToPolicyDialog } from './add-tenant-to-policy-dialog.component'; import { AddTenantToPolicyDialog } from './add-tenant-to-policy-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AddTenantToPolicyDialogRequest } from '../../../state/access-policy'; import { AddTenantToPolicyDialogRequest } from '../../../state/access-policy';
describe('AddTenantToPolicyDialog', () => { describe('AddTenantToPolicyDialog', () => {
@ -68,7 +68,7 @@ describe('AddTenantToPolicyDialog', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [AddTenantToPolicyDialog, BrowserAnimationsModule], imports: [AddTenantToPolicyDialog, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(AddTenantToPolicyDialog); fixture = TestBed.createComponent(AddTenantToPolicyDialog);

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OverridePolicyDialog } from './override-policy-dialog.component'; import { OverridePolicyDialog } from './override-policy-dialog.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('OverridePolicyDialog', () => { describe('OverridePolicyDialog', () => {
let component: OverridePolicyDialog; let component: OverridePolicyDialog;
@ -26,7 +26,7 @@ describe('OverridePolicyDialog', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [OverridePolicyDialog, BrowserAnimationsModule] imports: [OverridePolicyDialog, NoopAnimationsModule]
}); });
fixture = TestBed.createComponent(OverridePolicyDialog); fixture = TestBed.createComponent(OverridePolicyDialog);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -23,7 +23,7 @@ import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer'; import { initialState } from '../../../../../state/flow/flow.reducer';
import { CreateConnectionDialogRequest } from '../../../../../state/flow'; import { CreateConnectionDialogRequest } from '../../../../../state/flow';
import { ComponentType, DocumentedType } from '../../../../../../../state/shared'; import { ComponentType, DocumentedType } from '../../../../../../../state/shared';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { of } from 'rxjs'; import { of } from 'rxjs';
describe('CreateConnection', () => { describe('CreateConnection', () => {
@ -366,7 +366,7 @@ describe('CreateConnection', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CreateConnection, BrowserAnimationsModule], imports: [CreateConnection, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(CreateConnection); fixture = TestBed.createComponent(CreateConnection);

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DestinationProcessGroup } from './destination-process-group.component'; import { DestinationProcessGroup } from './destination-process-group.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('DestinationProcessGroup', () => { describe('DestinationProcessGroup', () => {
let component: DestinationProcessGroup; let component: DestinationProcessGroup;
@ -26,7 +26,7 @@ describe('DestinationProcessGroup', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [BrowserAnimationsModule, DestinationProcessGroup] imports: [NoopAnimationsModule, DestinationProcessGroup]
}); });
fixture = TestBed.createComponent(DestinationProcessGroup); fixture = TestBed.createComponent(DestinationProcessGroup);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DestinationRemoteProcessGroup } from './destination-remote-process-group.component'; import { DestinationRemoteProcessGroup } from './destination-remote-process-group.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('DestinationRemoteProcessGroup', () => { describe('DestinationRemoteProcessGroup', () => {
let component: DestinationRemoteProcessGroup; let component: DestinationRemoteProcessGroup;
@ -26,7 +26,7 @@ describe('DestinationRemoteProcessGroup', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [BrowserAnimationsModule, DestinationRemoteProcessGroup] imports: [NoopAnimationsModule, DestinationRemoteProcessGroup]
}); });
fixture = TestBed.createComponent(DestinationRemoteProcessGroup); fixture = TestBed.createComponent(DestinationRemoteProcessGroup);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -24,7 +24,7 @@ import { ComponentType } from '../../../../../../../state/shared';
import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer'; import { initialState } from '../../../../../state/flow/flow.reducer';
import { selectPrioritizerTypes } from '../../../../../../../state/extension-types/extension-types.selectors'; import { selectPrioritizerTypes } from '../../../../../../../state/extension-types/extension-types.selectors';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('EditConnectionComponent', () => { describe('EditConnectionComponent', () => {
let store: MockStore; let store: MockStore;
@ -129,7 +129,7 @@ describe('EditConnectionComponent', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditConnectionComponent, BrowserAnimationsModule], imports: [EditConnectionComponent, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SourceProcessGroup } from './source-process-group.component'; import { SourceProcessGroup } from './source-process-group.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('SourceProcessGroup', () => { describe('SourceProcessGroup', () => {
let component: SourceProcessGroup; let component: SourceProcessGroup;
@ -26,7 +26,7 @@ describe('SourceProcessGroup', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [BrowserAnimationsModule, SourceProcessGroup] imports: [NoopAnimationsModule, SourceProcessGroup]
}); });
fixture = TestBed.createComponent(SourceProcessGroup); fixture = TestBed.createComponent(SourceProcessGroup);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SourceRemoteProcessGroup } from './source-remote-process-group.component'; import { SourceRemoteProcessGroup } from './source-remote-process-group.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('SourceRemoteProcessGroup', () => { describe('SourceRemoteProcessGroup', () => {
let component: SourceRemoteProcessGroup; let component: SourceRemoteProcessGroup;
@ -26,7 +26,7 @@ describe('SourceRemoteProcessGroup', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [BrowserAnimationsModule, SourceRemoteProcessGroup] imports: [NoopAnimationsModule, SourceRemoteProcessGroup]
}); });
fixture = TestBed.createComponent(SourceRemoteProcessGroup); fixture = TestBed.createComponent(SourceRemoteProcessGroup);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -23,7 +23,7 @@ import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ComponentType } from '../../../../../../../state/shared'; import { ComponentType } from '../../../../../../../state/shared';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer'; import { initialState } from '../../../../../state/flow/flow.reducer';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { EMPTY } from 'rxjs'; import { EMPTY } from 'rxjs';
describe('ImportFromRegistry', () => { describe('ImportFromRegistry', () => {
@ -111,7 +111,7 @@ describe('ImportFromRegistry', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ImportFromRegistry, BrowserAnimationsModule], imports: [ImportFromRegistry, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(ImportFromRegistry); fixture = TestBed.createComponent(ImportFromRegistry);

View File

@ -23,7 +23,7 @@ import { ComponentType } from '../../../../../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer'; import { initialState } from '../../../../../state/flow/flow.reducer';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('CreatePort', () => { describe('CreatePort', () => {
let component: CreatePort; let component: CreatePort;
@ -43,7 +43,7 @@ describe('CreatePort', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CreatePort, BrowserAnimationsModule], imports: [CreatePort, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(CreatePort); fixture = TestBed.createComponent(CreatePort);

View File

@ -23,7 +23,7 @@ import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ComponentType } from '../../../../../../../state/shared'; import { ComponentType } from '../../../../../../../state/shared';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer'; import { initialState } from '../../../../../state/flow/flow.reducer';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('EditPort', () => { describe('EditPort', () => {
let component: EditPort; let component: EditPort;
@ -95,7 +95,7 @@ describe('EditPort', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditPort, BrowserAnimationsModule], imports: [EditPort, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(EditPort); fixture = TestBed.createComponent(EditPort);

View File

@ -23,7 +23,7 @@ import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ComponentType } from '../../../../../../../state/shared'; import { ComponentType } from '../../../../../../../state/shared';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer'; import { initialState } from '../../../../../state/flow/flow.reducer';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('CreateProcessGroup', () => { describe('CreateProcessGroup', () => {
let component: CreateProcessGroup; let component: CreateProcessGroup;
@ -155,7 +155,7 @@ describe('CreateProcessGroup', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CreateProcessGroup, BrowserAnimationsModule], imports: [CreateProcessGroup, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(CreateProcessGroup); fixture = TestBed.createComponent(CreateProcessGroup);

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditProcessGroup } from './edit-process-group.component'; import { EditProcessGroup } from './edit-process-group.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('EditProcessGroup', () => { describe('EditProcessGroup', () => {
let component: EditProcessGroup; let component: EditProcessGroup;
@ -106,7 +106,7 @@ describe('EditProcessGroup', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditProcessGroup, BrowserAnimationsModule], imports: [EditProcessGroup, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(EditProcessGroup); fixture = TestBed.createComponent(EditProcessGroup);

View File

@ -23,7 +23,7 @@ import { ComponentType } from '../../../../../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer'; import { initialState } from '../../../../../state/flow/flow.reducer';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('GroupComponents', () => { describe('GroupComponents', () => {
let component: GroupComponents; let component: GroupComponents;
@ -860,7 +860,7 @@ describe('GroupComponents', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [GroupComponents, BrowserAnimationsModule], imports: [GroupComponents, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(GroupComponents); fixture = TestBed.createComponent(GroupComponents);

View File

@ -23,7 +23,7 @@ import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../../../state/extension-types/extension-types.reducer'; import { initialState } from '../../../../../../../state/extension-types/extension-types.reducer';
import { ComponentType } from '../../../../../../../state/shared'; import { ComponentType } from '../../../../../../../state/shared';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('CreateProcessor', () => { describe('CreateProcessor', () => {
let component: CreateProcessor; let component: CreateProcessor;
@ -59,7 +59,7 @@ describe('CreateProcessor', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CreateProcessor, BrowserAnimationsModule], imports: [CreateProcessor, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(CreateProcessor); fixture = TestBed.createComponent(CreateProcessor);

View File

@ -21,7 +21,7 @@ import { EditProcessor } from './edit-processor.component';
import { EditComponentDialogRequest } from '../../../../../state/flow'; import { EditComponentDialogRequest } from '../../../../../state/flow';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ComponentType } from '../../../../../../../state/shared'; import { ComponentType } from '../../../../../../../state/shared';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../../../state/error/error.reducer'; import { initialState } from '../../../../../../../state/error/error.reducer';
@ -731,7 +731,7 @@ describe('EditProcessor', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditProcessor, MockErrorBanner, BrowserAnimationsModule], imports: [EditProcessor, MockErrorBanner, NoopAnimationsModule],
providers: [ providers: [
{ provide: MAT_DIALOG_DATA, useValue: data }, { provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ provideMockStore({

View File

@ -26,7 +26,7 @@ import { RouterModule } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('LoginForm', () => { describe('LoginForm', () => {
let component: LoginForm; let component: LoginForm;
@ -37,7 +37,7 @@ describe('LoginForm', () => {
declarations: [LoginForm], declarations: [LoginForm],
imports: [ imports: [
HttpClientTestingModule, HttpClientTestingModule,
BrowserAnimationsModule, NoopAnimationsModule,
MatFormFieldModule, MatFormFieldModule,
RouterModule, RouterModule,
RouterTestingModule, RouterTestingModule,

View File

@ -21,7 +21,7 @@ import { EditParameterContext } from './edit-parameter-context.component';
import { EditParameterContextRequest, ParameterContextEntity } from '../../../state/parameter-context-listing'; import { EditParameterContextRequest, ParameterContextEntity } from '../../../state/parameter-context-listing';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../state/parameter-context-listing/parameter-context-listing.reducer'; import { initialState } from '../../../state/parameter-context-listing/parameter-context-listing.reducer';
@ -234,7 +234,7 @@ describe('EditParameterContext', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditParameterContext, BrowserAnimationsModule], imports: [EditParameterContext, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(EditParameterContext); fixture = TestBed.createComponent(EditParameterContext);

View File

@ -18,7 +18,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { NiFiCommon } from '../../../service/nifi-common.service';
import { ProvenanceRequest } from '../state/provenance-event-listing'; import { ProvenanceRequest } from '../state/provenance-event-listing';
import { LineageRequest } from '../state/lineage'; import { LineageRequest } from '../state/lineage';
@ -26,10 +25,7 @@ import { LineageRequest } from '../state/lineage';
export class ProvenanceService { export class ProvenanceService {
private static readonly API: string = '../nifi-api'; private static readonly API: string = '../nifi-api';
constructor( constructor(private httpClient: HttpClient) {}
private httpClient: HttpClient,
private nifiCommon: NiFiCommon
) {}
getSearchOptions(): Observable<any> { getSearchOptions(): Observable<any> {
return this.httpClient.get(`${ProvenanceService.API}/provenance/search-options`); return this.httpClient.get(`${ProvenanceService.API}/provenance/search-options`);

View File

@ -66,7 +66,7 @@ export interface Lineage {
} }
export interface LineageState { export interface LineageState {
lineage: Lineage | null; activeLineage: Lineage | null;
error: string | null; completedLineage: Lineage;
status: 'pending' | 'loading' | 'error' | 'success'; status: 'pending' | 'loading' | 'error' | 'success';
} }

View File

@ -40,6 +40,8 @@ export const stopPollingLineageQuery = createAction('[Lineage] Stop Polling Line
export const deleteLineageQuery = createAction('[Lineage] Delete Lineage Query'); export const deleteLineageQuery = createAction('[Lineage] Delete Lineage Query');
export const deleteLineageQuerySuccess = createAction('[Lineage] Delete Lineage Query Success');
export const lineageApiError = createAction( export const lineageApiError = createAction(
'[Lineage] Load Parameter Context Listing Error', '[Lineage] Load Parameter Context Listing Error',
props<{ error: string }>() props<{ error: string }>()

View File

@ -18,15 +18,18 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects';
import * as LineageActions from './lineage.actions'; import * as LineageActions from './lineage.actions';
import * as ProvenanceActions from '../provenance-event-listing/provenance-event-listing.actions'; import { asyncScheduler, catchError, filter, from, interval, map, of, switchMap, takeUntil, tap } from 'rxjs';
import { asyncScheduler, catchError, from, interval, map, NEVER, of, switchMap, takeUntil, tap } from 'rxjs';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { NiFiState } from '../../../../state'; import { NiFiState } from '../../../../state';
import { ProvenanceService } from '../../service/provenance.service'; import { ProvenanceService } from '../../service/provenance.service';
import { Lineage } from './index'; import { Lineage } from './index';
import { selectClusterNodeId } from '../provenance-event-listing/provenance-event-listing.selectors'; import { selectClusterNodeId } from '../provenance-event-listing/provenance-event-listing.selectors';
import { selectLineageId } from './lineage.selectors'; import { selectActiveLineageId } from './lineage.selectors';
import * as ErrorActions from '../../../../state/error/error.actions';
import { ErrorHelper } from '../../../../service/error-helper.service';
import { HttpErrorResponse } from '@angular/common/http';
import { isDefinedAndNotNull } from '../../../../state/shared';
@Injectable() @Injectable()
export class LineageEffects { export class LineageEffects {
@ -34,6 +37,7 @@ export class LineageEffects {
private actions$: Actions, private actions$: Actions,
private store: Store<NiFiState>, private store: Store<NiFiState>,
private provenanceService: ProvenanceService, private provenanceService: ProvenanceService,
private errorHelper: ErrorHelper,
private dialog: MatDialog private dialog: MatDialog
) {} ) {}
@ -50,19 +54,18 @@ export class LineageEffects {
} }
}) })
), ),
catchError((error) => { catchError((errorResponse: HttpErrorResponse) => {
this.store.dispatch( if (this.errorHelper.showErrorInContext(errorResponse.status)) {
ProvenanceActions.showOkDialog({ return of(
title: 'Error', LineageActions.lineageApiError({
message: error.error error: errorResponse.error
}) })
); );
} else {
this.store.dispatch(LineageActions.stopPollingLineageQuery());
return of( return of(this.errorHelper.fullScreenError(errorResponse));
LineageActions.lineageApiError({ }
error: error.error
})
);
}) })
) )
) )
@ -100,29 +103,34 @@ export class LineageEffects {
pollLineageQuery$ = createEffect(() => pollLineageQuery$ = createEffect(() =>
this.actions$.pipe( this.actions$.pipe(
ofType(LineageActions.pollLineageQuery), ofType(LineageActions.pollLineageQuery),
concatLatestFrom(() => [this.store.select(selectLineageId), this.store.select(selectClusterNodeId)]), concatLatestFrom(() => [
switchMap(([, id, clusterNodeId]) => { this.store.select(selectActiveLineageId).pipe(isDefinedAndNotNull()),
if (id) { this.store.select(selectClusterNodeId)
return from(this.provenanceService.getLineageQuery(id, clusterNodeId)).pipe( ]),
map((response) => switchMap(([, id, clusterNodeId]) =>
LineageActions.pollLineageQuerySuccess({ from(this.provenanceService.getLineageQuery(id, clusterNodeId)).pipe(
response: { map((response) =>
lineage: response.lineage LineageActions.pollLineageQuerySuccess({
} response: {
}) lineage: response.lineage
), }
catchError((error) => })
of( ),
catchError((errorResponse: HttpErrorResponse) => {
if (this.errorHelper.showErrorInContext(errorResponse.status)) {
return of(
LineageActions.lineageApiError({ LineageActions.lineageApiError({
error: error.error error: errorResponse.error
}) })
) );
) } else {
); this.store.dispatch(LineageActions.stopPollingLineageQuery());
} else {
return NEVER; return of(this.errorHelper.fullScreenError(errorResponse));
} }
}) })
)
)
) )
); );
@ -130,15 +138,8 @@ export class LineageEffects {
this.actions$.pipe( this.actions$.pipe(
ofType(LineageActions.pollLineageQuerySuccess), ofType(LineageActions.pollLineageQuerySuccess),
map((action) => action.response), map((action) => action.response),
switchMap((response) => { filter((response) => response.lineage.finished),
const query: Lineage = response.lineage; switchMap(() => of(LineageActions.stopPollingLineageQuery()))
if (query.finished) {
this.dialog.closeAll();
return of(LineageActions.stopPollingLineageQuery());
} else {
return NEVER;
}
})
) )
); );
@ -149,17 +150,26 @@ export class LineageEffects {
) )
); );
deleteLineageQuery$ = createEffect( deleteLineageQuery$ = createEffect(() =>
() => this.actions$.pipe(
this.actions$.pipe( ofType(LineageActions.deleteLineageQuery),
ofType(LineageActions.deleteLineageQuery), concatLatestFrom(() => [this.store.select(selectActiveLineageId), this.store.select(selectClusterNodeId)]),
concatLatestFrom(() => [this.store.select(selectLineageId), this.store.select(selectClusterNodeId)]), tap(([, id, clusterNodeId]) => {
tap(([, id, clusterNodeId]) => { if (id) {
if (id) { this.provenanceService.deleteLineageQuery(id, clusterNodeId).subscribe();
this.provenanceService.deleteLineageQuery(id, clusterNodeId).subscribe(); }
} }),
}) switchMap(() => of(LineageActions.deleteLineageQuerySuccess()))
), )
{ dispatch: false } );
lineageApiError$ = createEffect(() =>
this.actions$.pipe(
ofType(LineageActions.lineageApiError),
tap(() => {
this.store.dispatch(LineageActions.stopPollingLineageQuery());
}),
switchMap(({ error }) => of(ErrorActions.addBannerError({ error })))
)
); );
} }

View File

@ -18,16 +18,32 @@
import { createReducer, on } from '@ngrx/store'; import { createReducer, on } from '@ngrx/store';
import { LineageState } from './index'; import { LineageState } from './index';
import { import {
deleteLineageQuerySuccess,
lineageApiError, lineageApiError,
pollLineageQuerySuccess, pollLineageQuerySuccess,
resetLineage, resetLineage,
submitLineageQuery, submitLineageQuery,
submitLineageQuerySuccess submitLineageQuerySuccess
} from './lineage.actions'; } from './lineage.actions';
import { produce } from 'immer';
export const initialState: LineageState = { export const initialState: LineageState = {
lineage: null, activeLineage: null,
error: null, completedLineage: {
id: '',
uri: '',
submissionTime: '',
expiration: '',
percentCompleted: 0,
finished: false,
request: {
lineageRequestType: 'FLOWFILE'
},
results: {
nodes: [],
links: []
}
},
status: 'pending' status: 'pending'
}; };
@ -40,15 +56,24 @@ export const lineageReducer = createReducer(
...state, ...state,
status: 'loading' as const status: 'loading' as const
})), })),
on(submitLineageQuerySuccess, pollLineageQuerySuccess, (state, { response }) => ({ on(submitLineageQuerySuccess, pollLineageQuerySuccess, (state, { response }) => {
return produce(state, (draftState) => {
const lineage = response.lineage;
draftState.activeLineage = lineage;
// if the query has finished save it as completed, the active query will be reset after deletion
if (lineage.finished) {
draftState.completedLineage = lineage;
draftState.status = 'success' as const;
}
});
}),
on(deleteLineageQuerySuccess, (state) => ({
...state, ...state,
lineage: response.lineage, activeLineage: null
error: null,
status: 'success' as const
})), })),
on(lineageApiError, (state, { error }) => ({ on(lineageApiError, (state) => ({
...state, ...state,
error,
status: 'error' as const status: 'error' as const
})) }))
); );

View File

@ -24,8 +24,11 @@ export const selectLineageState = createSelector(
(state: ProvenanceState) => state[lineageFeatureKey] (state: ProvenanceState) => state[lineageFeatureKey]
); );
export const selectStatus = createSelector(selectLineageState, (state: LineageState) => state.status); export const selectActiveLineage = createSelector(selectLineageState, (state: LineageState) => state.activeLineage);
export const selectLineage = createSelector(selectLineageState, (state: LineageState) => state.lineage); export const selectCompletedLineage = createSelector(
selectLineageState,
(state: LineageState) => state.completedLineage
);
export const selectLineageId = createSelector(selectLineage, (state: Lineage | null) => state?.id); export const selectActiveLineageId = createSelector(selectActiveLineage, (state: Lineage | null) => state?.id);

View File

@ -103,8 +103,8 @@ export interface Provenance {
export interface ProvenanceEventListingState { export interface ProvenanceEventListingState {
options: ProvenanceOptions | null; options: ProvenanceOptions | null;
request: ProvenanceRequest | null; request: ProvenanceRequest | null;
provenance: Provenance | null; activeProvenance: Provenance | null;
completedProvenance: Provenance;
loadedTimestamp: string; loadedTimestamp: string;
error: string | null;
status: 'pending' | 'loading' | 'error' | 'success'; status: 'pending' | 'loading' | 'error' | 'success';
} }

View File

@ -61,6 +61,8 @@ export const stopPollingProvenanceQuery = createAction('[Provenance Event Listin
export const deleteProvenanceQuery = createAction('[Provenance Event Listing] Delete Provenance Query'); export const deleteProvenanceQuery = createAction('[Provenance Event Listing] Delete Provenance Query');
export const deleteProvenanceQuerySuccess = createAction('[Provenance Event Listing] Delete Provenance Query Success');
export const provenanceApiError = createAction( export const provenanceApiError = createAction(
'[Provenance Event Listing] Provenance Api Error', '[Provenance Event Listing] Provenance Api Error',
props<{ error: string }>() props<{ error: string }>()

View File

@ -18,7 +18,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects';
import * as ProvenanceEventListingActions from './provenance-event-listing.actions'; import * as ProvenanceEventListingActions from './provenance-event-listing.actions';
import { asyncScheduler, catchError, from, interval, map, NEVER, of, switchMap, take, takeUntil, tap } from 'rxjs'; import { asyncScheduler, catchError, filter, from, interval, map, of, switchMap, take, takeUntil, tap } from 'rxjs';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { NiFiState } from '../../../../state'; import { NiFiState } from '../../../../state';
@ -27,7 +27,7 @@ import { OkDialog } from '../../../../ui/common/ok-dialog/ok-dialog.component';
import { ProvenanceService } from '../../service/provenance.service'; import { ProvenanceService } from '../../service/provenance.service';
import { import {
selectClusterNodeId, selectClusterNodeId,
selectProvenanceId, selectActiveProvenanceId,
selectProvenanceOptions, selectProvenanceOptions,
selectProvenanceRequest, selectProvenanceRequest,
selectTimeOffset selectTimeOffset
@ -37,6 +37,10 @@ import { ProvenanceSearchDialog } from '../../ui/provenance-event-listing/proven
import { selectAbout } from '../../../../state/about/about.selectors'; import { selectAbout } from '../../../../state/about/about.selectors';
import { ProvenanceEventDialog } from '../../../../ui/common/provenance-event-dialog/provenance-event-dialog.component'; import { ProvenanceEventDialog } from '../../../../ui/common/provenance-event-dialog/provenance-event-dialog.component';
import { CancelDialog } from '../../../../ui/common/cancel-dialog/cancel-dialog.component'; import { CancelDialog } from '../../../../ui/common/cancel-dialog/cancel-dialog.component';
import * as ErrorActions from '../../../../state/error/error.actions';
import { ErrorHelper } from '../../../../service/error-helper.service';
import { HttpErrorResponse } from '@angular/common/http';
import { isDefinedAndNotNull } from '../../../../state/shared';
@Injectable() @Injectable()
export class ProvenanceEventListingEffects { export class ProvenanceEventListingEffects {
@ -44,6 +48,7 @@ export class ProvenanceEventListingEffects {
private actions$: Actions, private actions$: Actions,
private store: Store<NiFiState>, private store: Store<NiFiState>,
private provenanceService: ProvenanceService, private provenanceService: ProvenanceService,
private errorHelper: ErrorHelper,
private dialog: MatDialog, private dialog: MatDialog,
private router: Router private router: Router
) {} ) {}
@ -58,10 +63,14 @@ export class ProvenanceEventListingEffects {
response response
}) })
), ),
catchError((error) => catchError(() =>
of( of(
ProvenanceEventListingActions.provenanceApiError({ ProvenanceEventListingActions.loadProvenanceOptionsSuccess({
error: error.error response: {
provenanceOptions: {
searchableFields: []
}
}
}) })
) )
) )
@ -74,38 +83,6 @@ export class ProvenanceEventListingEffects {
this.actions$.pipe( this.actions$.pipe(
ofType(ProvenanceEventListingActions.submitProvenanceQuery), ofType(ProvenanceEventListingActions.submitProvenanceQuery),
map((action) => action.request), map((action) => action.request),
switchMap((request) =>
from(this.provenanceService.submitProvenanceQuery(request)).pipe(
map((response) =>
ProvenanceEventListingActions.submitProvenanceQuerySuccess({
response: {
provenance: response.provenance
}
})
),
catchError((error) => {
this.store.dispatch(
ProvenanceEventListingActions.showOkDialog({
title: 'Error',
message: error.error
})
);
return of(
ProvenanceEventListingActions.provenanceApiError({
error: error.error
})
);
})
)
)
)
);
resubmitProvenanceQuery = createEffect(() =>
this.actions$.pipe(
ofType(ProvenanceEventListingActions.resubmitProvenanceQuery),
map((action) => action.request),
switchMap((request) => { switchMap((request) => {
const dialogReference = this.dialog.open(CancelDialog, { const dialogReference = this.dialog.open(CancelDialog, {
data: { data: {
@ -120,6 +97,37 @@ export class ProvenanceEventListingEffects {
this.store.dispatch(ProvenanceEventListingActions.stopPollingProvenanceQuery()); this.store.dispatch(ProvenanceEventListingActions.stopPollingProvenanceQuery());
}); });
return from(this.provenanceService.submitProvenanceQuery(request)).pipe(
map((response) =>
ProvenanceEventListingActions.submitProvenanceQuerySuccess({
response: {
provenance: response.provenance
}
})
),
catchError((errorResponse: HttpErrorResponse) => {
if (this.errorHelper.showErrorInContext(errorResponse.status)) {
return of(
ProvenanceEventListingActions.provenanceApiError({
error: errorResponse.error
})
);
} else {
this.store.dispatch(ProvenanceEventListingActions.stopPollingProvenanceQuery());
return of(this.errorHelper.fullScreenError(errorResponse));
}
})
);
})
)
);
resubmitProvenanceQuery = createEffect(() =>
this.actions$.pipe(
ofType(ProvenanceEventListingActions.resubmitProvenanceQuery),
map((action) => action.request),
switchMap((request) => {
return of(ProvenanceEventListingActions.submitProvenanceQuery({ request })); return of(ProvenanceEventListingActions.submitProvenanceQuery({ request }));
}) })
) )
@ -156,29 +164,34 @@ export class ProvenanceEventListingEffects {
pollProvenanceQuery$ = createEffect(() => pollProvenanceQuery$ = createEffect(() =>
this.actions$.pipe( this.actions$.pipe(
ofType(ProvenanceEventListingActions.pollProvenanceQuery), ofType(ProvenanceEventListingActions.pollProvenanceQuery),
concatLatestFrom(() => [this.store.select(selectProvenanceId), this.store.select(selectClusterNodeId)]), concatLatestFrom(() => [
switchMap(([, id, clusterNodeId]) => { this.store.select(selectActiveProvenanceId).pipe(isDefinedAndNotNull()),
if (id) { this.store.select(selectClusterNodeId)
return from(this.provenanceService.getProvenanceQuery(id, clusterNodeId)).pipe( ]),
map((response) => switchMap(([, id, clusterNodeId]) =>
ProvenanceEventListingActions.pollProvenanceQuerySuccess({ from(this.provenanceService.getProvenanceQuery(id, clusterNodeId)).pipe(
response: { map((response) =>
provenance: response.provenance ProvenanceEventListingActions.pollProvenanceQuerySuccess({
} response: {
}) provenance: response.provenance
), }
catchError((error) => })
of( ),
catchError((errorResponse: HttpErrorResponse) => {
if (this.errorHelper.showErrorInContext(errorResponse.status)) {
return of(
ProvenanceEventListingActions.provenanceApiError({ ProvenanceEventListingActions.provenanceApiError({
error: error.error error: errorResponse.error
}) })
) );
) } else {
); this.store.dispatch(ProvenanceEventListingActions.stopPollingProvenanceQuery());
} else {
return NEVER; return of(this.errorHelper.fullScreenError(errorResponse));
} }
}) })
)
)
) )
); );
@ -186,15 +199,8 @@ export class ProvenanceEventListingEffects {
this.actions$.pipe( this.actions$.pipe(
ofType(ProvenanceEventListingActions.pollProvenanceQuerySuccess), ofType(ProvenanceEventListingActions.pollProvenanceQuerySuccess),
map((action) => action.response), map((action) => action.response),
switchMap((response) => { filter((response) => response.provenance.finished),
const query: Provenance = response.provenance; switchMap(() => of(ProvenanceEventListingActions.stopPollingProvenanceQuery()))
if (query.finished) {
this.dialog.closeAll();
return of(ProvenanceEventListingActions.stopPollingProvenanceQuery());
} else {
return NEVER;
}
})
) )
); );
@ -205,18 +211,22 @@ export class ProvenanceEventListingEffects {
) )
); );
deleteProvenanceQuery$ = createEffect( deleteProvenanceQuery$ = createEffect(() =>
() => this.actions$.pipe(
this.actions$.pipe( ofType(ProvenanceEventListingActions.deleteProvenanceQuery),
ofType(ProvenanceEventListingActions.deleteProvenanceQuery), concatLatestFrom(() => [
concatLatestFrom(() => [this.store.select(selectProvenanceId), this.store.select(selectClusterNodeId)]), this.store.select(selectActiveProvenanceId),
tap(([, id, clusterNodeId]) => { this.store.select(selectClusterNodeId)
if (id) { ]),
this.provenanceService.deleteProvenanceQuery(id, clusterNodeId).subscribe(); tap(([, id, clusterNodeId]) => {
} this.dialog.closeAll();
})
), if (id) {
{ dispatch: false } this.provenanceService.deleteProvenanceQuery(id, clusterNodeId).subscribe();
}
}),
switchMap(() => of(ProvenanceEventListingActions.deleteProvenanceQuerySuccess()))
)
); );
openSearchDialog$ = createEffect( openSearchDialog$ = createEffect(
@ -227,44 +237,40 @@ export class ProvenanceEventListingEffects {
this.store.select(selectTimeOffset), this.store.select(selectTimeOffset),
this.store.select(selectProvenanceOptions), this.store.select(selectProvenanceOptions),
this.store.select(selectProvenanceRequest), this.store.select(selectProvenanceRequest),
this.store.select(selectAbout) this.store.select(selectAbout).pipe(isDefinedAndNotNull())
]), ]),
tap(([, timeOffset, options, currentRequest, about]) => { tap(([, timeOffset, options, currentRequest, about]) => {
if (about) { const dialogReference = this.dialog.open(ProvenanceSearchDialog, {
const dialogReference = this.dialog.open(ProvenanceSearchDialog, { data: {
data: { timeOffset,
timeOffset, options,
options, currentRequest
currentRequest },
}, panelClass: 'large-dialog'
panelClass: 'large-dialog' });
});
dialogReference.componentInstance.timezone = about.timezone; dialogReference.componentInstance.timezone = about.timezone;
dialogReference.componentInstance.submitSearchCriteria dialogReference.componentInstance.submitSearchCriteria
.pipe(take(1)) .pipe(take(1))
.subscribe((request: ProvenanceRequest) => { .subscribe((request: ProvenanceRequest) => {
if (request.searchTerms) { if (request.searchTerms) {
const queryParams: any = {}; const queryParams: any = {};
if (request.searchTerms['ProcessorID']) { if (request.searchTerms['ProcessorID']) {
queryParams['componentId'] = request.searchTerms['ProcessorID'].value; queryParams['componentId'] = request.searchTerms['ProcessorID'].value;
} }
if (request.searchTerms['FlowFileUUID']) { if (request.searchTerms['FlowFileUUID']) {
queryParams['flowFileUuid'] = request.searchTerms['FlowFileUUID'].value; queryParams['flowFileUuid'] = request.searchTerms['FlowFileUUID'].value;
}
// if either of the supported query params are present in the query, update the url
if (Object.keys(queryParams).length > 0) {
this.router.navigate(['/provenance'], { queryParams });
}
} }
this.store.dispatch(ProvenanceEventListingActions.saveProvenanceRequest({ request })); // if either of the supported query params are present in the query, update the url
}); if (Object.keys(queryParams).length > 0) {
} this.router.navigate(['/provenance'], { queryParams });
}
}
// TODO - if about hasn't loaded we should show an error this.store.dispatch(ProvenanceEventListingActions.saveProvenanceRequest({ request }));
});
}) })
), ),
{ dispatch: false } { dispatch: false }
@ -311,18 +317,31 @@ export class ProvenanceEventListingEffects {
dialogReference.componentInstance.replay dialogReference.componentInstance.replay
.pipe(takeUntil(dialogReference.afterClosed())) .pipe(takeUntil(dialogReference.afterClosed()))
.subscribe(() => { .subscribe(() => {
this.provenanceService.replay(request.id).subscribe(() => { dialogReference.close();
this.store.dispatch(
ProvenanceEventListingActions.showOkDialog({ this.provenanceService.replay(request.id).subscribe({
title: 'Provenance', next: () => {
message: 'Successfully submitted replay request.' this.store.dispatch(
}) ProvenanceEventListingActions.showOkDialog({
); title: 'Provenance',
message: 'Successfully submitted replay request.'
})
);
},
error: (errorResponse: HttpErrorResponse) => {
this.store.dispatch(
ErrorActions.snackBarError({ error: errorResponse.error })
);
}
}); });
}); });
}, },
error: () => { error: (errorResponse: HttpErrorResponse) => {
// TODO - handle error if (this.errorHelper.showErrorInContext(errorResponse.status)) {
this.store.dispatch(ErrorActions.snackBarError({ error: errorResponse.error }));
} else {
this.store.dispatch(this.errorHelper.fullScreenError(errorResponse));
}
} }
}); });
}) })
@ -337,9 +356,14 @@ export class ProvenanceEventListingEffects {
map((action) => action.request), map((action) => action.request),
tap((request) => { tap((request) => {
if (request.eventId) { if (request.eventId) {
this.provenanceService.getProvenanceEvent(request.eventId).subscribe((response) => { this.provenanceService.getProvenanceEvent(request.eventId).subscribe({
const event: any = response.provenanceEvent; next: (response) => {
this.router.navigate(this.getEventComponentLink(event.groupId, event.componentId)); const event: any = response.provenanceEvent;
this.router.navigate(this.getEventComponentLink(event.groupId, event.componentId));
},
error: (errorResponse: HttpErrorResponse) => {
this.store.dispatch(ErrorActions.snackBarError({ error: errorResponse.error }));
}
}); });
} else if (request.groupId && request.componentId) { } else if (request.groupId && request.componentId) {
this.router.navigate(this.getEventComponentLink(request.groupId, request.componentId)); this.router.navigate(this.getEventComponentLink(request.groupId, request.componentId));
@ -349,6 +373,16 @@ export class ProvenanceEventListingEffects {
{ dispatch: false } { dispatch: false }
); );
provenanceApiError$ = createEffect(() =>
this.actions$.pipe(
ofType(ProvenanceEventListingActions.provenanceApiError),
tap(() => {
this.store.dispatch(ProvenanceEventListingActions.stopPollingProvenanceQuery());
}),
switchMap(({ error }) => of(ErrorActions.addBannerError({ error })))
)
);
showOkDialog$ = createEffect( showOkDialog$ = createEffect(
() => () =>
this.actions$.pipe( this.actions$.pipe(

View File

@ -19,6 +19,7 @@ import { createReducer, on } from '@ngrx/store';
import { ProvenanceEventListingState } from './index'; import { ProvenanceEventListingState } from './index';
import { import {
clearProvenanceRequest, clearProvenanceRequest,
deleteProvenanceQuerySuccess,
loadProvenanceOptionsSuccess, loadProvenanceOptionsSuccess,
pollProvenanceQuerySuccess, pollProvenanceQuerySuccess,
provenanceApiError, provenanceApiError,
@ -27,13 +28,35 @@ import {
submitProvenanceQuery, submitProvenanceQuery,
submitProvenanceQuerySuccess submitProvenanceQuerySuccess
} from './provenance-event-listing.actions'; } from './provenance-event-listing.actions';
import { produce } from 'immer';
export const initialState: ProvenanceEventListingState = { export const initialState: ProvenanceEventListingState = {
options: null, options: null,
request: null, request: null,
provenance: null, activeProvenance: null,
loadedTimestamp: '', completedProvenance: {
error: null, id: '',
uri: '',
submissionTime: '',
expiration: '',
percentCompleted: 0,
finished: false,
request: {
maxResults: 0,
summarize: true,
incrementalResults: false
},
results: {
provenanceEvents: [],
total: '',
totalCount: 0,
generated: 'N/A',
oldestEvent: 'N/A',
timeOffset: 0,
errors: []
}
},
loadedTimestamp: 'N/A',
status: 'pending' status: 'pending'
}; };
@ -50,12 +73,22 @@ export const provenanceEventListingReducer = createReducer(
...state, ...state,
status: 'loading' as const status: 'loading' as const
})), })),
on(submitProvenanceQuerySuccess, pollProvenanceQuerySuccess, (state, { response }) => ({ on(submitProvenanceQuerySuccess, pollProvenanceQuerySuccess, (state, { response }) => {
return produce(state, (draftState) => {
const provenance = response.provenance;
draftState.activeProvenance = provenance;
// if the query has finished save it as completed, the active query will be reset after deletion
if (provenance.finished) {
draftState.completedProvenance = provenance;
draftState.loadedTimestamp = provenance.results.generated;
draftState.status = 'success' as const;
}
});
}),
on(deleteProvenanceQuerySuccess, (state) => ({
...state, ...state,
provenance: response.provenance, activeProvenance: null
loadedTimestamp: response.provenance.results.generated,
error: null,
status: 'success' as const
})), })),
on(saveProvenanceRequest, (state, { request }) => ({ on(saveProvenanceRequest, (state, { request }) => ({
...state, ...state,
@ -65,9 +98,8 @@ export const provenanceEventListingReducer = createReducer(
...state, ...state,
request: null request: null
})), })),
on(provenanceApiError, (state, { error }) => ({ on(provenanceApiError, (state) => ({
...state, ...state,
error,
status: 'error' as const status: 'error' as const
})) }))
); );

View File

@ -66,19 +66,27 @@ export const selectLoadedTimestamp = createSelector(
(state: ProvenanceEventListingState) => state.loadedTimestamp (state: ProvenanceEventListingState) => state.loadedTimestamp
); );
export const selectProvenance = createSelector( export const selectActiveProvenance = createSelector(
selectProvenanceEventListingState, selectProvenanceEventListingState,
(state: ProvenanceEventListingState) => state.provenance (state: ProvenanceEventListingState) => state.activeProvenance
); );
export const selectProvenanceId = createSelector(selectProvenance, (state: Provenance | null) => state?.id); export const selectCompletedProvenance = createSelector(
selectProvenanceEventListingState,
(state: ProvenanceEventListingState) => state.completedProvenance
);
export const selectActiveProvenanceId = createSelector(selectActiveProvenance, (state: Provenance | null) => state?.id);
export const selectClusterNodeId = createSelector( export const selectClusterNodeId = createSelector(
selectProvenanceRequest, selectProvenanceRequest,
(state: ProvenanceRequest | null) => state?.clusterNodeId (state: ProvenanceRequest | null) => state?.clusterNodeId
); );
export const selectProvenanceResults = createSelector(selectProvenance, (state: Provenance | null) => state?.results); export const selectProvenanceResults = createSelector(
selectCompletedProvenance,
(state: Provenance | null) => state?.results
);
export const selectTimeOffset = createSelector( export const selectTimeOffset = createSelector(
selectProvenanceResults, selectProvenanceResults,

View File

@ -33,6 +33,7 @@
(resubmitProvenanceQuery)="resubmitProvenanceQuery()" (resubmitProvenanceQuery)="resubmitProvenanceQuery()"
(clearRequest)="clearRequest()" (clearRequest)="clearRequest()"
(queryLineage)="queryLineage($event)" (queryLineage)="queryLineage($event)"
(clearBannerErrors)="clearBannerErrors()"
(resetLineage)="resetLineage()"></provenance-event-table> (resetLineage)="resetLineage()"></provenance-event-table>
</ng-container> </ng-container>
<ng-template #initialLoading> <ng-template #initialLoading>

View File

@ -27,7 +27,7 @@ import {
} from '../../state/provenance-event-listing'; } from '../../state/provenance-event-listing';
import { import {
selectLoadedTimestamp, selectLoadedTimestamp,
selectProvenance, selectCompletedProvenance,
selectProvenanceRequest, selectProvenanceRequest,
selectSearchableFieldsFromRoute, selectSearchableFieldsFromRoute,
selectStatus selectStatus
@ -46,7 +46,8 @@ import {
import { ProvenanceSearchDialog } from './provenance-search-dialog/provenance-search-dialog.component'; import { ProvenanceSearchDialog } from './provenance-search-dialog/provenance-search-dialog.component';
import { resetLineage, submitLineageQuery } from '../../state/lineage/lineage.actions'; import { resetLineage, submitLineageQuery } from '../../state/lineage/lineage.actions';
import { LineageRequest } from '../../state/lineage'; import { LineageRequest } from '../../state/lineage';
import { selectLineage } from '../../state/lineage/lineage.selectors'; import { selectCompletedLineage } from '../../state/lineage/lineage.selectors';
import { clearBannerErrors } from '../../../../state/error/error.actions';
@Component({ @Component({
selector: 'provenance-event-listing', selector: 'provenance-event-listing',
@ -56,8 +57,8 @@ import { selectLineage } from '../../state/lineage/lineage.selectors';
export class ProvenanceEventListing implements OnDestroy { export class ProvenanceEventListing implements OnDestroy {
status$ = this.store.select(selectStatus); status$ = this.store.select(selectStatus);
loadedTimestamp$ = this.store.select(selectLoadedTimestamp); loadedTimestamp$ = this.store.select(selectLoadedTimestamp);
provenance$ = this.store.select(selectProvenance); provenance$ = this.store.select(selectCompletedProvenance);
lineage$ = this.store.select(selectLineage); lineage$ = this.store.select(selectCompletedLineage);
request!: ProvenanceRequest; request!: ProvenanceRequest;
stateReset = false; stateReset = false;
@ -200,6 +201,10 @@ export class ProvenanceEventListing implements OnDestroy {
); );
} }
clearBannerErrors(): void {
this.store.dispatch(clearBannerErrors());
}
resetLineage(): void { resetLineage(): void {
this.store.dispatch(resetLineage()); this.store.dispatch(resetLineage());
} }
@ -208,5 +213,6 @@ export class ProvenanceEventListing implements OnDestroy {
this.stateReset = true; this.stateReset = true;
this.store.dispatch(resetProvenanceState()); this.store.dispatch(resetProvenanceState());
this.store.dispatch(resetLineage()); this.store.dispatch(resetLineage());
this.store.dispatch(clearBannerErrors());
} }
} }

View File

@ -15,7 +15,8 @@
~ limitations under the License. ~ limitations under the License.
--> -->
<div class="provenance-event-table h-full"> <div class="provenance-event-table h-full flex flex-col gap-y-2">
<error-banner></error-banner>
<div [class.hidden]="showLineage" class="h-full flex flex-col gap-y-2"> <div [class.hidden]="showLineage" class="h-full flex flex-col gap-y-2">
<div class="flex flex-col"> <div class="flex flex-col">
<div [class.invisible]="!filterApplied" class="value font-bold"> <div [class.invisible]="!filterApplied" class="value font-bold">

View File

@ -19,15 +19,30 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProvenanceEventTable } from './provenance-event-table.component'; import { ProvenanceEventTable } from './provenance-event-table.component';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/error/error.reducer';
import { Component } from '@angular/core';
describe('ProvenanceEventTable', () => { describe('ProvenanceEventTable', () => {
let component: ProvenanceEventTable; let component: ProvenanceEventTable;
let fixture: ComponentFixture<ProvenanceEventTable>; let fixture: ComponentFixture<ProvenanceEventTable>;
@Component({
selector: 'error-banner',
standalone: true,
template: ''
})
class MockErrorBanner {}
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ProvenanceEventTable, MatTableModule, BrowserAnimationsModule] imports: [ProvenanceEventTable, MockErrorBanner, MatTableModule, NoopAnimationsModule],
providers: [
provideMockStore({
initialState
})
]
}); });
fixture = TestBed.createComponent(ProvenanceEventTable); fixture = TestBed.createComponent(ProvenanceEventTable);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -38,6 +38,7 @@ import { LineageComponent } from './lineage/lineage.component';
import { GoToProvenanceEventSourceRequest, ProvenanceEventRequest } from '../../../state/provenance-event-listing'; import { GoToProvenanceEventSourceRequest, ProvenanceEventRequest } from '../../../state/provenance-event-listing';
import { MatSliderModule } from '@angular/material/slider'; import { MatSliderModule } from '@angular/material/slider';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
@Component({ @Component({
selector: 'provenance-event-table', selector: 'provenance-event-table',
@ -58,7 +59,8 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
AsyncPipe, AsyncPipe,
MatPaginatorModule, MatPaginatorModule,
LineageComponent, LineageComponent,
MatSliderModule MatSliderModule,
ErrorBanner
], ],
styleUrls: ['./provenance-event-table.component.scss'] styleUrls: ['./provenance-event-table.component.scss']
}) })
@ -149,6 +151,7 @@ export class ProvenanceEventTable implements AfterViewInit {
@Output() resubmitProvenanceQuery: EventEmitter<void> = new EventEmitter<void>(); @Output() resubmitProvenanceQuery: EventEmitter<void> = new EventEmitter<void>();
@Output() queryLineage: EventEmitter<LineageRequest> = new EventEmitter<LineageRequest>(); @Output() queryLineage: EventEmitter<LineageRequest> = new EventEmitter<LineageRequest>();
@Output() resetLineage: EventEmitter<void> = new EventEmitter<void>(); @Output() resetLineage: EventEmitter<void> = new EventEmitter<void>();
@Output() clearBannerErrors: EventEmitter<void> = new EventEmitter<void>();
protected readonly TextTip = TextTip; protected readonly TextTip = TextTip;
protected readonly BulletinsTip = BulletinsTip; protected readonly BulletinsTip = BulletinsTip;
@ -305,11 +308,7 @@ export class ProvenanceEventTable implements AfterViewInit {
return false; return false;
} }
if (event.componentId === 'Remote Output Port' || event.componentId === 'Remote Input Port') { return !(event.componentId === 'Remote Output Port' || event.componentId === 'Remote Input Port');
return false;
}
return true;
} }
goToClicked(event: ProvenanceEventSummary): void { goToClicked(event: ProvenanceEventSummary): void {
@ -327,6 +326,8 @@ export class ProvenanceEventTable implements AfterViewInit {
this.eventId = event.id; this.eventId = event.id;
this.showLineage = true; this.showLineage = true;
this.clearBannerErrors.next();
this.submitLineageQuery({ this.submitLineageQuery({
lineageRequestType: 'FLOWFILE', lineageRequestType: 'FLOWFILE',
uuid: event.flowFileUuid, uuid: event.flowFileUuid,
@ -345,6 +346,7 @@ export class ProvenanceEventTable implements AfterViewInit {
this.eventTimestampStep = 1; this.eventTimestampStep = 1;
this.initialEventTimestampThreshold = 0; this.initialEventTimestampThreshold = 0;
this.currentEventTimestampThreshold = 0; this.currentEventTimestampThreshold = 0;
this.clearBannerErrors.next();
this.resetLineage.next(); this.resetLineage.next();
} }

View File

@ -29,6 +29,11 @@
</div> </div>
</div> </div>
</ng-container> </ng-container>
<ng-container *ngIf="request.options.searchableFields.length === 0">
<div class="unset mb-5">
No searchable fields are available. Search criteria based on date, time, and file size still available.
</div>
</ng-container>
<mat-form-field> <mat-form-field>
<mat-label>Date Range</mat-label> <mat-label>Date Range</mat-label>
<mat-date-range-input [rangePicker]="picker"> <mat-date-range-input [rangePicker]="picker">

View File

@ -20,6 +20,10 @@
.search-events-form { .search-events-form {
@include mat.button-density(-1); @include mat.button-density(-1);
mat-dialog-content {
min-height: 450px;
}
.mat-mdc-form-field { .mat-mdc-form-field {
width: 100%; width: 100%;
} }

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProvenanceSearchDialog } from './provenance-search-dialog.component'; import { ProvenanceSearchDialog } from './provenance-search-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatNativeDateModule } from '@angular/material/core'; import { MatNativeDateModule } from '@angular/material/core';
describe('ProvenanceSearchDialog', () => { describe('ProvenanceSearchDialog', () => {
@ -71,7 +71,7 @@ describe('ProvenanceSearchDialog', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ProvenanceSearchDialog, BrowserAnimationsModule, MatNativeDateModule], imports: [ProvenanceSearchDialog, NoopAnimationsModule, MatNativeDateModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(ProvenanceSearchDialog); fixture = TestBed.createComponent(ProvenanceSearchDialog);

View File

@ -102,7 +102,7 @@ export interface FlowFileDialogRequest {
export interface QueueListingState { export interface QueueListingState {
activeListingRequest: ListingRequest | null; activeListingRequest: ListingRequest | null;
completedListingRequest: ListingRequest | null; completedListingRequest: ListingRequest;
connectionLabel: string; connectionLabel: string;
loadedTimestamp: string; loadedTimestamp: string;
status: 'pending' | 'loading' | 'error' | 'success'; status: 'pending' | 'loading' | 'error' | 'success';

View File

@ -66,13 +66,13 @@ export const queueListingReducer = createReducer(
on(submitQueueListingRequestSuccess, pollQueueListingRequestSuccess, (state, { response }) => { on(submitQueueListingRequestSuccess, pollQueueListingRequestSuccess, (state, { response }) => {
return produce(state, (draftState) => { return produce(state, (draftState) => {
const listingRequest = response.requestEntity.listingRequest; const listingRequest = response.requestEntity.listingRequest;
draftState.activeListingRequest = listingRequest;
// if the query has finished save it as completed, the active query will be reset after deletion
if (listingRequest.finished) { if (listingRequest.finished) {
draftState.completedListingRequest = listingRequest; draftState.completedListingRequest = listingRequest;
draftState.loadedTimestamp = listingRequest.lastUpdated; draftState.loadedTimestamp = listingRequest.lastUpdated;
draftState.status = 'success' as const; draftState.status = 'success' as const;
} else {
draftState.activeListingRequest = listingRequest;
} }
}); });
}), }),

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FlowFileDialog } from './flowfile-dialog.component'; import { FlowFileDialog } from './flowfile-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { FlowFileDialogRequest } from '../../../state/queue-listing'; import { FlowFileDialogRequest } from '../../../state/queue-listing';
describe('FlowFileDialog', () => { describe('FlowFileDialog', () => {
@ -46,7 +46,7 @@ describe('FlowFileDialog', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [FlowFileDialog, BrowserAnimationsModule], imports: [FlowFileDialog, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(FlowFileDialog); fixture = TestBed.createComponent(FlowFileDialog);

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FlowFileTable } from './flowfile-table.component'; import { FlowFileTable } from './flowfile-table.component';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/error/error.reducer'; import { initialState } from '../../../../../state/error/error.reducer';
@ -37,7 +37,7 @@ describe('FlowFileTable', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [FlowFileTable, MockErrorBanner, MatTableModule, BrowserAnimationsModule], imports: [FlowFileTable, MockErrorBanner, MatTableModule, NoopAnimationsModule],
providers: [ providers: [
provideMockStore({ provideMockStore({
initialState initialState

View File

@ -627,7 +627,9 @@ export class ParameterProvidersEffects {
concatLatestFrom(() => this.store.select(selectApplyParameterProviderParametersRequest)), concatLatestFrom(() => this.store.select(selectApplyParameterProviderParametersRequest)),
tap(([, updateRequest]) => { tap(([, updateRequest]) => {
if (updateRequest) { if (updateRequest) {
this.parameterProviderService.deleteParameterProviderParametersUpdateRequest(updateRequest); this.parameterProviderService
.deleteParameterProviderParametersUpdateRequest(updateRequest)
.subscribe();
} }
}) })
), ),

View File

@ -21,7 +21,7 @@ import { CreateFlowAnalysisRule } from './create-flow-analysis-rule.component';
import { CreateFlowAnalysisRuleDialogRequest } from '../../../state/flow-analysis-rules'; import { CreateFlowAnalysisRuleDialogRequest } from '../../../state/flow-analysis-rules';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { initialState } from '../../../state/flow-analysis-rules/flow-analysis-rules.reducer'; import { initialState } from '../../../state/flow-analysis-rules/flow-analysis-rules.reducer';
describe('CreateFlowAnalysisRule', () => { describe('CreateFlowAnalysisRule', () => {
@ -47,7 +47,7 @@ describe('CreateFlowAnalysisRule', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CreateFlowAnalysisRule, BrowserAnimationsModule], imports: [CreateFlowAnalysisRule, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(CreateFlowAnalysisRule); fixture = TestBed.createComponent(CreateFlowAnalysisRule);

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditFlowAnalysisRule } from './edit-flow-analysis-rule.component'; import { EditFlowAnalysisRule } from './edit-flow-analysis-rule.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { EditFlowAnalysisRuleDialogRequest } from '../../../state/flow-analysis-rules'; import { EditFlowAnalysisRuleDialogRequest } from '../../../state/flow-analysis-rules';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
@ -101,7 +101,7 @@ describe('EditFlowAnalysisRule', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditFlowAnalysisRule, MockErrorBanner, BrowserAnimationsModule], imports: [EditFlowAnalysisRule, MockErrorBanner, NoopAnimationsModule],
providers: [ providers: [
{ provide: MAT_DIALOG_DATA, useValue: data }, { provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ provideMockStore({

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FlowAnalysisRuleTable } from './flow-analysis-rule-table.component'; import { FlowAnalysisRuleTable } from './flow-analysis-rule-table.component';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('FlowAnalysisRuleTable', () => { describe('FlowAnalysisRuleTable', () => {
let component: FlowAnalysisRuleTable; let component: FlowAnalysisRuleTable;
@ -27,7 +27,7 @@ describe('FlowAnalysisRuleTable', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [BrowserAnimationsModule, MatTableModule, FlowAnalysisRuleTable] imports: [NoopAnimationsModule, MatTableModule, FlowAnalysisRuleTable]
}); });
fixture = TestBed.createComponent(FlowAnalysisRuleTable); fixture = TestBed.createComponent(FlowAnalysisRuleTable);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -23,7 +23,7 @@ import { initialState } from '../../../state/general/general.reducer';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('GeneralForm', () => { describe('GeneralForm', () => {
let component: GeneralForm; let component: GeneralForm;
@ -32,7 +32,7 @@ describe('GeneralForm', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [GeneralForm], declarations: [GeneralForm],
imports: [BrowserAnimationsModule, MatFormFieldModule, MatInputModule, FormsModule, ReactiveFormsModule], imports: [NoopAnimationsModule, MatFormFieldModule, MatInputModule, FormsModule, ReactiveFormsModule],
providers: [ providers: [
provideMockStore({ provideMockStore({
initialState initialState

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { CreateRegistryClient } from './create-registry-client.component'; import { CreateRegistryClient } from './create-registry-client.component';
import { CreateRegistryClientDialogRequest } from '../../../state/registry-clients'; import { CreateRegistryClientDialogRequest } from '../../../state/registry-clients';
@ -43,7 +43,7 @@ describe('CreateRegistryClient', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CreateRegistryClient, BrowserAnimationsModule], imports: [CreateRegistryClient, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(CreateRegistryClient); fixture = TestBed.createComponent(CreateRegistryClient);

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { EditRegistryClient } from './edit-registry-client.component'; import { EditRegistryClient } from './edit-registry-client.component';
import { EditRegistryClientDialogRequest } from '../../../state/registry-clients'; import { EditRegistryClientDialogRequest } from '../../../state/registry-clients';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@ -114,7 +114,7 @@ describe('EditRegistryClient', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditRegistryClient, MockErrorBanner, BrowserAnimationsModule], imports: [EditRegistryClient, MockErrorBanner, NoopAnimationsModule],
providers: [ providers: [
{ provide: MAT_DIALOG_DATA, useValue: data }, { provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ provideMockStore({

View File

@ -20,7 +20,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RegistryClientTable } from './registry-client-table.component'; import { RegistryClientTable } from './registry-client-table.component';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort'; import { MatSortModule } from '@angular/material/sort';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('RegistryClientTable', () => { describe('RegistryClientTable', () => {
let component: RegistryClientTable; let component: RegistryClientTable;
@ -29,7 +29,7 @@ describe('RegistryClientTable', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [RegistryClientTable], declarations: [RegistryClientTable],
imports: [MatTableModule, MatSortModule, BrowserAnimationsModule] imports: [MatTableModule, MatSortModule, NoopAnimationsModule]
}); });
fixture = TestBed.createComponent(RegistryClientTable); fixture = TestBed.createComponent(RegistryClientTable);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -22,7 +22,7 @@ import { CreateReportingTaskDialogRequest } from '../../../state/reporting-tasks
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/extension-types/extension-types.reducer'; import { initialState } from '../../../../../state/extension-types/extension-types.reducer';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('CreateReportingTask', () => { describe('CreateReportingTask', () => {
let component: CreateReportingTask; let component: CreateReportingTask;
@ -46,7 +46,7 @@ describe('CreateReportingTask', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CreateReportingTask, BrowserAnimationsModule], imports: [CreateReportingTask, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(CreateReportingTask); fixture = TestBed.createComponent(CreateReportingTask);

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditReportingTask } from './edit-reporting-task.component'; import { EditReportingTask } from './edit-reporting-task.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { EditReportingTaskDialogRequest } from '../../../state/reporting-tasks'; import { EditReportingTaskDialogRequest } from '../../../state/reporting-tasks';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
@ -394,7 +394,7 @@ describe('EditReportingTask', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditReportingTask, MockErrorBanner, BrowserAnimationsModule], imports: [EditReportingTask, MockErrorBanner, NoopAnimationsModule],
providers: [ providers: [
{ provide: MAT_DIALOG_DATA, useValue: data }, { provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ provideMockStore({

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserAccessPolicies } from './user-access-policies.component'; import { UserAccessPolicies } from './user-access-policies.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { UserAccessPoliciesDialogRequest } from '../../../state/user-listing'; import { UserAccessPoliciesDialogRequest } from '../../../state/user-listing';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@ -52,7 +52,7 @@ describe('UserAccessPolicies', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [UserAccessPolicies, BrowserAnimationsModule], imports: [UserAccessPolicies, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(UserAccessPolicies); fixture = TestBed.createComponent(UserAccessPolicies);

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentStateDialog } from './component-state.component'; import { ComponentStateDialog } from './component-state.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../state/component-state/component-state.reducer'; import { initialState } from '../../../state/component-state/component-state.reducer';
@ -28,7 +28,7 @@ describe('ComponentStateDialog', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ComponentStateDialog, BrowserAnimationsModule], imports: [ComponentStateDialog, NoopAnimationsModule],
providers: [provideMockStore({ initialState })] providers: [provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(ComponentStateDialog); fixture = TestBed.createComponent(ComponentStateDialog);

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ControllerServiceTable } from './controller-service-table.component'; import { ControllerServiceTable } from './controller-service-table.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('ControllerServiceTable', () => { describe('ControllerServiceTable', () => {
let component: ControllerServiceTable; let component: ControllerServiceTable;
@ -26,7 +26,7 @@ describe('ControllerServiceTable', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [BrowserAnimationsModule, ControllerServiceTable] imports: [NoopAnimationsModule, ControllerServiceTable]
}); });
fixture = TestBed.createComponent(ControllerServiceTable); fixture = TestBed.createComponent(ControllerServiceTable);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -21,7 +21,7 @@ import { CreateControllerService } from './create-controller-service.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../state/extension-types/extension-types.reducer'; import { initialState } from '../../../../state/extension-types/extension-types.reducer';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { CreateControllerServiceDialogRequest } from '../../../../state/shared'; import { CreateControllerServiceDialogRequest } from '../../../../state/shared';
describe('CreateControllerService', () => { describe('CreateControllerService', () => {
@ -56,7 +56,7 @@ describe('CreateControllerService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CreateControllerService, BrowserAnimationsModule], imports: [CreateControllerService, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
}); });
fixture = TestBed.createComponent(CreateControllerService); fixture = TestBed.createComponent(CreateControllerService);

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DisableControllerService } from './disable-controller-service.component'; import { DisableControllerService } from './disable-controller-service.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../state/contoller-service-state/controller-service-state.reducer'; import { initialState } from '../../../../state/contoller-service-state/controller-service-state.reducer';
import { ComponentType, SetEnableControllerServiceDialogRequest } from '../../../../state/shared'; import { ComponentType, SetEnableControllerServiceDialogRequest } from '../../../../state/shared';
@ -341,7 +341,7 @@ describe('EnableControllerService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [DisableControllerService, BrowserAnimationsModule], imports: [DisableControllerService, NoopAnimationsModule],
providers: [provideMockStore({ initialState }), { provide: MAT_DIALOG_DATA, useValue: data }] providers: [provideMockStore({ initialState }), { provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(DisableControllerService); fixture = TestBed.createComponent(DisableControllerService);

View File

@ -20,7 +20,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditControllerService } from './edit-controller-service.component'; import { EditControllerService } from './edit-controller-service.component';
import { EditControllerServiceDialogRequest } from '../../../../state/shared'; import { EditControllerServiceDialogRequest } from '../../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../state/error/error.reducer'; import { initialState } from '../../../../state/error/error.reducer';
@ -553,7 +553,7 @@ describe('EditControllerService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditControllerService, MockErrorBanner, BrowserAnimationsModule], imports: [EditControllerService, MockErrorBanner, NoopAnimationsModule],
providers: [ providers: [
{ provide: MAT_DIALOG_DATA, useValue: data }, { provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ provideMockStore({

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EnableControllerService } from './enable-controller-service.component'; import { EnableControllerService } from './enable-controller-service.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../state/contoller-service-state/controller-service-state.reducer'; import { initialState } from '../../../../state/contoller-service-state/controller-service-state.reducer';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
@ -341,7 +341,7 @@ describe('EnableControllerService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EnableControllerService, BrowserAnimationsModule, MatDialogModule], imports: [EnableControllerService, NoopAnimationsModule, MatDialogModule],
providers: [provideMockStore({ initialState }), { provide: MAT_DIALOG_DATA, useValue: data }] providers: [provideMockStore({ initialState }), { provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(EnableControllerService); fixture = TestBed.createComponent(EnableControllerService);

View File

@ -20,7 +20,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditParameterDialog } from './edit-parameter-dialog.component'; import { EditParameterDialog } from './edit-parameter-dialog.component';
import { EditParameterRequest } from '../../../state/shared'; import { EditParameterRequest } from '../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('EditParameterDialog', () => { describe('EditParameterDialog', () => {
let component: EditParameterDialog; let component: EditParameterDialog;
@ -51,7 +51,7 @@ describe('EditParameterDialog', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditParameterDialog, BrowserAnimationsModule], imports: [EditParameterDialog, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(EditParameterDialog); fixture = TestBed.createComponent(EditParameterDialog);

View File

@ -20,7 +20,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditTenantDialog } from './edit-tenant-dialog.component'; import { EditTenantDialog } from './edit-tenant-dialog.component';
import { EditTenantRequest } from '../../../state/shared'; import { EditTenantRequest } from '../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing'; import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../state/error/error.reducer'; import { initialState } from '../../../state/error/error.reducer';
@ -794,7 +794,7 @@ describe('EditTenantDialog', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [EditTenantDialog, MockErrorBanner, BrowserAnimationsModule], imports: [EditTenantDialog, MockErrorBanner, NoopAnimationsModule],
providers: [ providers: [
{ provide: MAT_DIALOG_DATA, useValue: data }, { provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ provideMockStore({

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ExtensionCreation } from './extension-creation.component'; import { ExtensionCreation } from './extension-creation.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('ExtensionCreation', () => { describe('ExtensionCreation', () => {
let component: ExtensionCreation; let component: ExtensionCreation;
@ -26,7 +26,7 @@ describe('ExtensionCreation', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ExtensionCreation, BrowserAnimationsModule] imports: [ExtensionCreation, NoopAnimationsModule]
}); });
fixture = TestBed.createComponent(ExtensionCreation); fixture = TestBed.createComponent(ExtensionCreation);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -21,7 +21,7 @@ import { NewPropertyDialog } from './new-property-dialog.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NewPropertyDialogRequest } from '../../../state/shared'; import { NewPropertyDialogRequest } from '../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('NewPropertyDialog', () => { describe('NewPropertyDialog', () => {
let component: NewPropertyDialog; let component: NewPropertyDialog;
@ -34,7 +34,7 @@ describe('NewPropertyDialog', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NewPropertyDialog, BrowserAnimationsModule, FormsModule, ReactiveFormsModule], imports: [NewPropertyDialog, NoopAnimationsModule, FormsModule, ReactiveFormsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(NewPropertyDialog); fixture = TestBed.createComponent(NewPropertyDialog);

View File

@ -21,7 +21,7 @@ import { ComboEditor } from './combo-editor.component';
import { PropertyItem } from '../../property-table.component'; import { PropertyItem } from '../../property-table.component';
import { Parameter } from '../../../../../state/shared'; import { Parameter } from '../../../../../state/shared';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('ComboEditor', () => { describe('ComboEditor', () => {
let component: ComboEditor; let component: ComboEditor;
@ -73,7 +73,7 @@ describe('ComboEditor', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ComboEditor, BrowserAnimationsModule] imports: [ComboEditor, NoopAnimationsModule]
}); });
fixture = TestBed.createComponent(ComboEditor); fixture = TestBed.createComponent(ComboEditor);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -399,11 +399,7 @@
"></ng-container> "></ng-container>
</div> </div>
<div> <div>
<button <button color="primary" mat-stroked-button (click)="replayClicked()">
color="primary"
mat-stroked-button
mat-dialog-close
(click)="replayClicked()">
<i class="fa fa-repeat"></i> <i class="fa fa-repeat"></i>
Replay Replay
</button> </button>

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProvenanceEventDialog } from './provenance-event-dialog.component'; import { ProvenanceEventDialog } from './provenance-event-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('ProvenanceEventDialog', () => { describe('ProvenanceEventDialog', () => {
let component: ProvenanceEventDialog; let component: ProvenanceEventDialog;
@ -71,7 +71,7 @@ describe('ProvenanceEventDialog', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ProvenanceEventDialog, BrowserAnimationsModule], imports: [ProvenanceEventDialog, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
}); });
fixture = TestBed.createComponent(ProvenanceEventDialog); fixture = TestBed.createComponent(ProvenanceEventDialog);