Skip to content

Commit

Permalink
Merge pull request #366 from catenax-ng/feature/TRACEFOSS-1549-Create…
Browse files Browse the repository at this point in the history
…-alert-detailed-view

Feature/tracefoss 1549 create alert detailed view
  • Loading branch information
ds-mwesener authored Jul 10, 2023
2 parents 5d9222f + 623cec2 commit 55c5ffa
Show file tree
Hide file tree
Showing 25 changed files with 1,012 additions and 23 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

### Added
- Added back button in notification detailed view
- Added alert detail view
- EDC SPI Dependency for using provided models
- Added default response types to apis
- Irs policies support ( on application startup registers policies in irs instance )

### Changed

- Changed Layout in notification detailed view
- Changed request parameter for registerjob request to irs to match requirements of irs
- Migration of edc 0.4.1 endpoints and api flow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const buildMockAlerts = (
};
});

const MockEmptyAlert: NotificationResponse = {
export const MockEmptyAlert: NotificationResponse = {
id: `${AlertIdPrefix}000`,
description: `Alert No 000`,
status: NotificationStatus.CREATED,
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/app/modules/page/alerts/alerts.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { getI18nPageProvider } from '@core/i18n';
import { AlertsRoutingModule } from '@page/alerts/alerts.routing';
import { AlertDetailFacade } from '@page/alerts/core/alert-detail.facade';
import { AlertDetailState } from '@page/alerts/core/alert-detail.state';
import { AlertHelperService } from '@page/alerts/core/alert-helper.service';
import { AlertsFacade } from '@page/alerts/core/alerts.facade';
import { AlertsState } from '@page/alerts/core/alerts.state';
import { AlertDetailComponent } from '@page/alerts/detail/alert-detail.component';
import { PartsModule } from '@page/parts/parts.module';
import { NotificationModule } from '@shared/modules/notification/notification.module';
import { SharedModule } from '@shared/shared.module';
import { TemplateModule } from '@shared/template.module';
Expand All @@ -32,20 +36,23 @@ import { AlertsComponent } from './presentation/alerts.component';

@NgModule({
declarations: [
AlertsComponent
AlertsComponent, AlertDetailComponent
],
imports: [
CommonModule,
TemplateModule,
SharedModule,
AlertsRoutingModule,
NotificationModule,
PartsModule
],
providers: [
AlertsFacade,
AlertsState,
AlertDetailFacade,
AlertDetailState,
AlertHelperService,
...getI18nPageProvider('page.alerts'),
...getI18nPageProvider('page.alert'),
]
})
export class AlertsModule { }
11 changes: 10 additions & 1 deletion frontend/src/app/modules/page/alerts/alerts.routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AlertDetailComponent } from '@page/alerts/detail/alert-detail.component';
import { AlertsComponent } from '@page/alerts/presentation/alerts.component';
import { I18NEXT_NAMESPACE_RESOLVER } from 'angular-i18next';

Expand All @@ -28,7 +29,14 @@ const ALERTS_ROUTING: Routes = [
path: '',
pathMatch: 'full',
component: AlertsComponent,
data: { i18nextNamespaces: ['page.alerts'] },
data: { i18nextNamespaces: ['page.alert'] },
resolve: { i18next: I18NEXT_NAMESPACE_RESOLVER },
},
{
path: ':alertId',
pathMatch: 'full',
component: AlertDetailComponent,
data: { i18nextNamespaces: ['page.alert'] },
resolve: { i18next: I18NEXT_NAMESPACE_RESOLVER },
},
];
Expand All @@ -38,3 +46,4 @@ const ALERTS_ROUTING: Routes = [
exports: [RouterModule],
})
export class AlertsRoutingModule {}

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/********************************************************************************
* Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

import { TitleCasePipe } from '@angular/common';
import { TestBed } from '@angular/core/testing';
import { CalendarDateModel } from '@core/model/calendar-date.model';
import { AlertDetailFacade } from '@page/alerts/core/alert-detail.facade';
import { AlertDetailState } from '@page/alerts/core/alert-detail.state';
import { Notification } from '@shared/model/notification.model';
import { Severity } from '@shared/model/severity.model';
import { PartsService } from '@shared/service/parts.service';

describe('AlertDetailFacade', () => {
let alertDetailFacade: AlertDetailFacade;
let alertDetailState: AlertDetailState;
let partsService: jasmine.SpyObj<PartsService>;
let titleCasePipe: jasmine.SpyObj<TitleCasePipe>;

let testNotification: Notification = {
id: 'id-1',
description: 'Alert No 1',
createdBy: { name: 'OEM xxxxxxxxxxxxxxx A', bpn: 'BPN10000000OEM0A' },
sendTo: { name: 'OEM xxxxxxxxxxxxxxx B', bpn: 'BPN20000000OEM0B' },
reason: { close: '', accept: '', decline: '' },
isFromSender: true,
assetIds: [],
status: null,
severity: Severity.MINOR,
createdDate: new CalendarDateModel('2022-05-01T10:34:12.000Z'),
}

beforeEach(() => {
/*
alertDetailState = jasmine.createSpyObj('AlertDetailState', [
'getIdsFromPartList',
'setAlertPartsInformation',
'getAlertPartsInformation',
'setSupplierPartsInformation',
'getSupplierPartsInformation'
]);
*/
alertDetailState = new AlertDetailState();
partsService = jasmine.createSpyObj('PartsService', [ 'getPartDetailOfIds' ]);
titleCasePipe = jasmine.createSpyObj('TitleCasePipe', [ 'transform' ]);

TestBed.configureTestingModule({
providers: [
AlertDetailFacade,
{ provide: AlertDetailState, useValue: alertDetailState },
{ provide: PartsService, useValue: partsService },
{ provide: TitleCasePipe, useValue: titleCasePipe }
]
});

alertDetailFacade = TestBed.inject(AlertDetailFacade);
});

it('should create the component facade', () => {
expect(alertDetailFacade).toBeTruthy();
});

it('should set empty data if no asset ids are provided', () => {

let callSpy = spyOn(alertDetailFacade, 'setAlertPartsInformation');
let setSpy = spyOnProperty(alertDetailState, 'alertPartsInformation', 'set').and.callThrough();

alertDetailFacade.setAlertPartsInformation(testNotification);
expect(callSpy).toHaveBeenCalledWith(testNotification);

})
});
131 changes: 131 additions & 0 deletions frontend/src/app/modules/page/alerts/core/alert-detail.facade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/********************************************************************************
* Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

import { TitleCasePipe } from '@angular/common';
import { Injectable } from '@angular/core';
import { AlertDetailState } from '@page/alerts/core/alert-detail.state';
import { Part, SemanticDataModel } from '@page/parts/model/parts.model';
import { Notification } from '@shared/model/notification.model';
import { View } from '@shared/model/view.model';
import { PartsService } from '@shared/service/parts.service';
import { Observable, of, Subscription } from 'rxjs';
import { filter, map, switchMap } from 'rxjs/operators';
import { SortDirection } from '../../../../mocks/services/pagination.helper';

@Injectable()
export class AlertDetailFacade {
private notificationPartsInformationDescription: Subscription;
private supplierPartsSubscription: Subscription;

constructor(
private readonly partsService: PartsService,
private readonly alertDetailState: AlertDetailState,
private readonly titleCasePipe: TitleCasePipe,
) {
}

public get notificationPartsInformation$(): Observable<View<Part[]>> {
return this.alertDetailState.alertPartsInformation$;
}

public get supplierPartsInformation$(): Observable<View<Part[]>> {
return this.alertDetailState.supplierPartsInformation$;
}

public get selected$(): Observable<View<Notification>> {
return this.alertDetailState.selected$;
}

public set selected(selectedAlert: View<Notification>) {
this.alertDetailState.selected = selectedAlert;
}

public get selected(): View<Notification> {
return this.alertDetailState.selected;
}

public setAlertPartsInformation(notification: Notification): void {
this.notificationPartsInformationDescription?.unsubscribe();
this.alertDetailState.alertPartsInformation = { loader: true };

if (!notification.assetIds.length) {
this.alertDetailState.alertPartsInformation = { data: [] };
return;
}

this.notificationPartsInformationDescription = this.partsService
.getPartDetailOfIds(notification.assetIds)
.subscribe({
next: data => {
data.forEach(part => {
part.semanticDataModel = <SemanticDataModel>this.titleCasePipe.transform(part.semanticDataModel);
})
this.alertDetailState.alertPartsInformation = { data };
},
error: error => (this.alertDetailState.alertPartsInformation = { error }),
});
}

public setAndSupplierPartsInformation(): void {
this.supplierPartsSubscription?.unsubscribe();
this.alertDetailState.supplierPartsInformation = { loader: true };

this.supplierPartsSubscription = this.alertDetailState.alertPartsInformation$
.pipe(
filter(view => !!view.data),
map(({ data }) => this.getIdsFromPartList(data)),
switchMap(partIds => (!!partIds && !!partIds.length ? this.partsService.getPartDetailOfIds(partIds) : of([]))),
)
.subscribe({
next: data => {
data.forEach(part => {
part.semanticDataModel = this.titleCasePipe.transform(part.semanticDataModel);
})
this.alertDetailState.supplierPartsInformation = { data };
},
error: error => (this.alertDetailState.supplierPartsInformation = { error }),
});
}

public sortNotificationParts(key: string, direction: SortDirection): void {
const { data } = this.alertDetailState.alertPartsInformation;
if (!data) return;

const sortedData = this.partsService.sortParts(data, key, direction);
this.alertDetailState.alertPartsInformation = { data: [ ...sortedData ] };
}

public sortSupplierParts(key: string, direction: SortDirection): void {
const { data } = this.alertDetailState.supplierPartsInformation;
if (!data) return;

const sortedData = this.partsService.sortParts(data, key, direction);
this.alertDetailState.supplierPartsInformation = { data: [ ...sortedData ] };
}

public unsubscribeSubscriptions(): void {
this.notificationPartsInformationDescription?.unsubscribe();
this.supplierPartsSubscription?.unsubscribe();
}

private getIdsFromPartList(parts: Part[]): string[] {
const childIds = parts.map(part => part.children).reduce((p, c) => [ ...p, ...c ], []);
return [ ...new Set(childIds) ];
}
}
73 changes: 73 additions & 0 deletions frontend/src/app/modules/page/alerts/core/alert-detail.state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/********************************************************************************
* Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

import { Injectable } from '@angular/core';
import { Part } from '@page/parts/model/parts.model';
import { Notification } from '@shared/model/notification.model';
import { State } from '@shared/model/state';
import { View } from '@shared/model/view.model';
import { Observable } from 'rxjs';

@Injectable()
export class AlertDetailState {
private readonly _alertPartsInformation$ = new State<View<Part[]>>({ loader: true });
private readonly _supplierPartsInformation$ = new State<View<Part[]>>({ loader: true });

private readonly _selected$ = new State<View<Notification>>({ loader: true });

// Detailed information for parts assigned to an alert
public get alertPartsInformation$(): Observable<View<Part[]>> {
return this._alertPartsInformation$.observable;
}

public get alertPartsInformation(): View<Part[]> {
return this._alertPartsInformation$.snapshot;
}

public set alertPartsInformation(view: View<Part[]>) {
this._alertPartsInformation$.update(view);
}

// Detailed information for child parts assigned to an alert
public get supplierPartsInformation$(): Observable<View<Part[]>> {
return this._supplierPartsInformation$.observable;
}

public get supplierPartsInformation(): View<Part[]> {
return this._supplierPartsInformation$.snapshot;
}

public set supplierPartsInformation(view: View<Part[]>) {
this._supplierPartsInformation$.update(view);
}

// Selected Notification
public get selected$(): Observable<View<Notification>> {
return this._selected$.observable;
}

public set selected({ data, loader, error }: View<Notification>) {
const view: View<Notification> = { data, loader, error };
this._selected$.update(view);
}

get selected(): View<Notification> {
return this._selected$.snapshot;
}
}
Loading

0 comments on commit 55c5ffa

Please sign in to comment.