Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Bug/#1009 try again button fix #1136

Merged
merged 11 commits into from
Jul 3, 2024
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ _**For better traceability add the corresponding GitHub issue number in each cha
- #639 handle expired or incorrect policies when sending notifications
- #786 Added authorization as admin for submodel api & registry api
- #884 Upgraded tractionBatteryCode from 1.0.0 to 2.0.0
- #1009 reimplemented retry request logic for notification approval
- #786 Added alternative port (only accessible within same cluster) for application which is used for unsecured API endpoints.
- #994 improved bpn edc configuration view uux


### Added
- #832 added policymanagement list view, creator and editor
- #737 Added concept: Contract table -> parts link action
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/app/modules/core/api/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { AuthService } from '../auth/auth.service';
providedIn: 'root',
})
export class ApiService {
lastRequest: { execute?: () => Observable<void>, context?: any };
constructor(private readonly httpClient: HttpClient, private readonly authService: AuthService) {
}

Expand Down Expand Up @@ -111,6 +112,16 @@ export class ApiService {
});
}

/**
* set the public class property 'lastRequest' from where you made the request
* before retrying
*/
public retryLastRequest(): Observable<any> | null {
if (this.lastRequest.execute) {
return this.lastRequest.execute();
}
}

private buildHeaders(): HttpHeaders {
return new HttpHeaders({
Access: 'application/json',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,22 @@
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { MockedKeycloakService } from '@core/auth/mocked-keycloak.service';
import { TableSettingsService } from '@core/user/table-settings.service';
import { TableType } from '@shared/components/multi-select-autocomplete/table-type.model';
import { TableViewConfig } from '@shared/components/parts-table/table-view-config.model';
import { ToastService } from '@shared/components/toasts/toast.service';
import { KeycloakService } from 'keycloak-angular';

describe('TableSettingsService', () => {
let service: TableSettingsService;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
providers: [ TableSettingsService ],
imports: [ HttpClientTestingModule ],
providers: [ TableSettingsService, ToastService, { provide: KeycloakService, useValue: MockedKeycloakService } ],
});
service = TestBed.inject(TableSettingsService);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@
</ng-template>

<ng-template #reasonInfoTmp let-view="view">
<app-notification-reason [notification]="view.data"></app-notification-reason>
<app-notification-reason [notificationMessages]="view.data?.messages"></app-notification-reason>
</ng-template>

<ng-template #errorTmp let-view="view">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
import { ActivatedRoute } from '@angular/router';
import { NotificationDetailComponent } from '@page/notifications/detail/notification-detail.component';
import { NotificationsModule } from '@page/notifications/notifications.module';
import { NotificationAssembler } from '@shared/assembler/notification.assembler';
import { NotificationService } from '@shared/service/notification.service';
import { screen, waitFor } from '@testing-library/angular';
import { renderComponent } from '@tests/test-render.utils';
import { of } from 'rxjs';
import { MockEmptyAlert } from '../../../../mocks/services/alerts-mock/alerts.test.model';

describe('NotificationDetailComponent', () => {

Expand Down Expand Up @@ -58,4 +60,23 @@ describe('NotificationDetailComponent', () => {
await waitFor(() => expect(screen.getByText('actions.goBack')).toBeInTheDocument());
});

it('should correctly behave on toast retry action', async () => {
const { fixture } = await renderNotificationDetail('id-1');
const { componentInstance } = fixture;
const ngSpy = spyOn(componentInstance, 'ngAfterViewInit').and.returnValue(null);
const toastSuccessSpy = spyOn(componentInstance['toastService'], 'success');
const toastErrorSpy = spyOn(componentInstance['toastService'], 'error');

componentInstance.selectedNotification = NotificationAssembler.assembleNotification(MockEmptyAlert);
componentInstance['toastService'].retryAction.emit({ success: true });
expect(toastSuccessSpy).toHaveBeenCalled();

componentInstance['toastService'].retryAction.emit({ error: true });
expect(toastErrorSpy).toHaveBeenCalled();

expect(ngSpy).toHaveBeenCalled();

});


});
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy {
public selectedNotification: Notification;

private paramSubscription: Subscription;
private toastActionSubscription: Subscription;

constructor(
public readonly helperService: NotificationHelperService,
Expand All @@ -81,6 +82,18 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy {
this.notificationPartsInformation$ = this.notificationDetailFacade.notificationPartsInformation$;
this.supplierPartsDetailInformation$ = this.notificationDetailFacade.supplierPartsInformation$;

this.toastActionSubscription = this.toastService.retryAction.subscribe({
next: result => {
const formattedStatus = result?.context?.charAt(0)?.toUpperCase() + result?.context?.slice(1)?.toLowerCase();
if (result?.success) {
this.toastService.success(`requestNotification.successfully${ formattedStatus }`);
} else if (result?.error) {
this.toastService.error(`requestNotification.failed${ formattedStatus }`, 15000, true);
}
this.ngAfterViewInit();
},
});

this.selected$ = this.notificationDetailFacade.selected$;

this.paramSubscription = this.route.queryParams.subscribe(params => {
Expand Down Expand Up @@ -111,6 +124,7 @@ export class NotificationDetailComponent implements AfterViewInit, OnDestroy {
this.subscription?.unsubscribe();
this.notificationDetailFacade.unsubscribeSubscriptions();
this.paramSubscription?.unsubscribe();
this.toastActionSubscription?.unsubscribe();
}

public navigateToEditView() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,21 @@ describe('NotificationsComponent', () => {
expect(notificationsComponent['notificationReceivedSortList']).toEqual([]);
});

it('should correctly behave on toast retry action', async () => {
const { fixture } = await renderNotifications();
const { componentInstance } = fixture;
const handleConfirmSpy = spyOn(componentInstance, 'handleConfirmActionCompletedEvent');
const toastSuccessSpy = spyOn(componentInstance['toastService'], 'success');
const toastErrorSpy = spyOn(componentInstance['toastService'], 'error');

componentInstance['toastService'].retryAction.emit({ success: true });
expect(toastSuccessSpy).toHaveBeenCalled();

componentInstance['toastService'].retryAction.emit({ error: true });
expect(toastErrorSpy).toHaveBeenCalled();

expect(handleConfirmSpy).toHaveBeenCalled();

});

});
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { NotificationChannel } from '@shared/components/multi-select-autocomplet
import { NotificationCommonModalComponent } from '@shared/components/notification-common-modal/notification-common-modal.component';
import { TableSortingUtil } from '@shared/components/table/table-sorting.util';
import { MenuActionConfig, TableEventConfig, TableHeaderSort } from '@shared/components/table/table.model';
import { ToastService } from '@shared/components/toasts/toast.service';
import { createDeeplinkNotificationFilter } from '@shared/helper/notification-helper';
import { setMultiSorting } from '@shared/helper/table-helper';
import { NotificationTabInformation } from '@shared/model/notification-tab-information';
Expand Down Expand Up @@ -58,6 +59,7 @@ export class NotificationsComponent {
private ctrlKeyState: boolean = false;

private paramSubscription: Subscription;
private toastActionSubscription: Subscription;

receivedFilter: NotificationFilter;
requestedFilter: NotificationFilter;
Expand All @@ -71,6 +73,7 @@ export class NotificationsComponent {
private readonly router: Router,
private readonly route: ActivatedRoute,
private readonly cd: ChangeDetectorRef,
private readonly toastService: ToastService,
) {
this.notificationsReceived$ = this.notificationsFacade.notificationsReceived$;
this.notificationsQueuedAndRequested$ = this.notificationsFacade.notificationsQueuedAndRequested$;
Expand All @@ -81,6 +84,18 @@ export class NotificationsComponent {
window.addEventListener('keyup', (event) => {
this.ctrlKeyState = setMultiSorting(event);
});

this.toastActionSubscription = this.toastService.retryAction.subscribe({
next: result => {
const formatted = result?.context?.charAt(0)?.toUpperCase() + result?.context?.slice(1)?.toLowerCase();
if (result?.success) {
this.toastService.success(`requestNotification.successfully${ formatted }`);
} else if (result?.error) {
this.toastService.error(`requestNotification.failed${ formatted }`, 15000, true);
}
this.handleConfirmActionCompletedEvent();
},
});
}

public ngOnInit(): void {
Expand All @@ -106,6 +121,7 @@ export class NotificationsComponent {
public ngOnDestroy(): void {
this.notificationsFacade.stopNotifications();
this.paramSubscription?.unsubscribe();
this.toastActionSubscription?.unsubscribe();
}

public onReceivedTableConfigChange(pagination: TableEventConfig) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ describe('NotificationReasonComponent', () => {
},
]
};
component.notification = notification;
component.notificationMessages = notification.messages;
component.ngOnInit();
expect(component.textMessages.length).toBe(2);
expect(component.textMessages[0].message).toEqual('Hello');
expect(component.textMessages[1].direction).toEqual('left');
Expand Down Expand Up @@ -102,7 +103,7 @@ describe('NotificationReasonComponent', () => {
isFromSender: true,
messages: []
};
component.notification = notification;
component.notificationMessages = notification.messages;
expect(component.textMessages.length).toBe(0);
});

Expand All @@ -122,7 +123,7 @@ describe('NotificationReasonComponent', () => {
isFromSender: true,
messages: []
};
component.notification = notification;
component.notificationMessages = notification.messages;
// Since date is invalid, sorting and processing might behave unexpectedly, expecting no error thrown and no messages processed
expect(component.textMessages.length).toBe(0);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import { Component, Input } from '@angular/core';
import { environment } from '@env';
import { Notification, NotificationStatus } from '@shared/model/notification.model';
import { NotificationMessage, NotificationStatus } from '@shared/model/notification.model';

type TextMessageDirection = 'left' | 'right';

Expand All @@ -42,31 +42,26 @@ interface TextMessage {
})
export class NotificationReasonComponent {
public textMessages: TextMessage[] = [];
@Input() notificationMessages: NotificationMessage[];

@Input() set notification({
description,
status,
isFromSender,
createdDate,
createdBy,
createdByName,
sendTo,
sendToName,
messages,
}: Notification) {
ngOnInit() {
if (!this.notificationMessages) {
return;
}
let sortedMessagesAfterDates = [ ...this.notificationMessages ];

const sortedMessagesAfterDates = messages.sort((a, b) => new Date(a.messageDate).valueOf() - new Date(b.messageDate).valueOf());
sortedMessagesAfterDates.sort((a, b) => new Date(a.messageDate).valueOf() - new Date(b.messageDate).valueOf());

sortedMessagesAfterDates.forEach(message => {
this.textMessages.push({
message: message.message,
direction: environment.bpn === message.sentBy ? 'right' : 'left',
user: message.sentByName,
bpn: message.sentBy,
status: message.status,
date: message.messageDate,
errorMessage: message.errorMessage,
});
sortedMessagesAfterDates?.forEach(message => {
this.textMessages.push({
message: message.message,
direction: environment.bpn === message.sentBy ? 'right' : 'left',
user: message.sentByName,
bpn: message.sentBy,
status: message.status,
date: message.messageDate,
errorMessage: message.errorMessage,
});
});

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
********************************************************************************/

import { EventEmitter, Injectable } from '@angular/core';
import { ApiService } from '@core/api/api.service';
import { I18nMessage } from '@shared/model/i18n-message';
import { Observable, Subject } from 'rxjs';
import { CallAction, ToastMessage, ToastStatus } from './toast-message/toast-message.model';
Expand All @@ -32,6 +33,9 @@ export class ToastService {
private idx = 0;
retryAction = new EventEmitter<any>();

constructor(private readonly apiService: ApiService) {
}

public getCurrentToast$(): Observable<ToastMessage> {
return this.toastStore.asObservable();
}
Expand All @@ -56,7 +60,10 @@ export class ToastService {
this.toastStore.next(new ToastMessage(this.idx++, message, ToastStatus.Warning, timeout));
}

public emitClick(event?: any) {
this.retryAction.emit();
public emitClick(): void {
this.apiService.retryLastRequest()?.subscribe({
next: (next) => this.retryAction.emit({ success: next, context: this.apiService.lastRequest?.context }),
error: (err) => this.retryAction.emit({ error: err, context: this.apiService.lastRequest?.context }),
});
};
}
18 changes: 11 additions & 7 deletions frontend/src/app/modules/shared/components/toasts/toast.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,19 @@
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

import {TestBed} from '@angular/core/testing';
import {SharedModule} from '@shared/shared.module';
import {screen} from '@testing-library/angular';
import {renderComponent} from '@tests/test-render.utils';
import {ToastService} from './toast.service';
import { TestBed } from '@angular/core/testing';
import { ApiService } from '@core/api/api.service';
import { SharedModule } from '@shared/shared.module';
import { screen } from '@testing-library/angular';
import { renderComponent } from '@tests/test-render.utils';
import { ToastService } from './toast.service';

describe('toasts', () => {
const renderToastLayout = async () => {
await renderComponent(`<app-toast-container></app-toast-container>`, { imports: [ SharedModule ] });
await renderComponent(`<app-toast-container></app-toast-container>`, {
imports: [ SharedModule ],
providers: [ ApiService ],
});
return TestBed.inject(ToastService);
};

Expand Down Expand Up @@ -68,7 +72,7 @@ describe('toasts', () => {

it('should emit click action on toast', async () => {
const toastService = await renderToastLayout();
const toastActionSpy = spyOn(toastService.retryAction, 'emit')
const toastActionSpy = spyOn(toastService['apiService'], 'retryLastRequest');
toastService.emitClick();
expect(toastActionSpy).toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</mat-card>
<mat-card class="max-h-74 overflow-y-scroll">
<mat-card-content>
<app-notification-reason [notification]="notification"></app-notification-reason>
<app-notification-reason [notificationMessages]="notification?.messages"></app-notification-reason>
</mat-card-content>
</mat-card>
</div>
Loading
Loading