From 02478b8078d29396858d1471960fae39b9e5e878 Mon Sep 17 00:00:00 2001 From: "Vila,Jordi (IT EDP)" Date: Tue, 9 Jun 2026 09:58:56 +0200 Subject: [PATCH 1/2] Fix double toast behavior on user init --- src/app/app.component.spec.ts | 9 +++++++++ src/app/app.component.ts | 12 +++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index ccaa605..b2b9893 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -279,6 +279,15 @@ describe('AppComponent', () => { natsLiveMessage$.next({data: {}} as NatsMessage); await fixture.whenStable(); expect(mockToastService.showToast).not.toHaveBeenCalled(); + + // Suppression window should short-circuit before validation and toast rendering. + component['suppressLiveToastUntil'] = Date.now() + 5000; + mockNatsService.isValidMessage.and.returnValue(true); + natsLiveMessage$.next({data: {}} as NatsMessage); + await fixture.whenStable(); + expect(mockToastService.showToast).not.toHaveBeenCalled(); + + component['suppressLiveToastUntil'] = 0; mockNatsService.isValidMessage.and.returnValue(true); natsLiveMessage$.next({data: {}} as NatsMessage); await fixture.whenStable(); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index ff5257f..c25d722 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -33,6 +33,8 @@ export class AppComponent implements OnInit, OnDestroy { private readonly natsUrl: string | undefined; private unreadMessagesCountSubscription: Subscription | undefined; private liveMessageSubscription: Subscription | undefined; + private hasShownInitialNotificationToast = false; + private suppressLiveToastUntil = 0; headerVariant: string = AppShellConfig.headerVariant; applicationLogo: string = AppShellConfig.applicationLogo; applicationName: string = AppShellConfig.applicationName; @@ -334,9 +336,11 @@ export class AppComponent implements OnInit, OnDestroy { // validBucketRe = regexp.MustCompile(^[a-zA-Z0-9_-]+$) // validKeyRe = regexp.MustCompile(^[-/_=.a-zA-Z0-9]+$) const natsUser = user.username.split('@')[0].replaceAll(/[^a-zA-Z0-9_-]/g, '_') + this.hasShownInitialNotificationToast = false; + this.suppressLiveToastUntil = 0; this.natsService.initializeUser(natsUser, user.projects).then(() => { setTimeout(() => { - if(this.appShellNotificationsCount > 0) { + if(this.appShellNotificationsCount > 0 && !this.hasShownInitialNotificationToast) { const notification = { id: Date.now().toString() + '-logged', title: `You have ${this.appShellNotificationsCount} unread notifications`, @@ -344,6 +348,8 @@ export class AppComponent implements OnInit, OnDestroy { subject: 'only-toast' } as AppShellNotification; this.toastService.showToast(notification, 8000); + this.hasShownInitialNotificationToast = true; + this.suppressLiveToastUntil = Date.now() + 5000; } }, 1000); }); @@ -358,6 +364,9 @@ export class AppComponent implements OnInit, OnDestroy { if (!message?.data) { return; } + if (Date.now() < this.suppressLiveToastUntil) { + return; + } try { if (this.natsService.isValidMessage(message.data)) { console.log('Received valid message:', message); @@ -371,6 +380,7 @@ export class AppComponent implements OnInit, OnDestroy { }; // If you want to show the actual notification, you can show message.data instead of notification this.toastService.showToast(notification, 8000); + this.hasShownInitialNotificationToast = true; } else { console.log('Invalid message format:', message); } From 6e0ad1aeb7089e1d4a27161d64d66122c8f2c464 Mon Sep 17 00:00:00 2001 From: "Vila,Jordi (IT EDP)" Date: Tue, 9 Jun 2026 10:31:49 +0200 Subject: [PATCH 2/2] Fix race condition when accessing a catalog by url --- src/app/services/catalog-resolver.service.spec.ts | 12 +++++++++--- src/app/services/catalog-resolver.service.ts | 8 ++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/app/services/catalog-resolver.service.spec.ts b/src/app/services/catalog-resolver.service.spec.ts index a3203e2..db98bfa 100644 --- a/src/app/services/catalog-resolver.service.spec.ts +++ b/src/app/services/catalog-resolver.service.spec.ts @@ -10,7 +10,7 @@ describe('CatalogResolver', () => { let catalogServiceSpy: jasmine.SpyObj; beforeEach(() => { - catalogServiceSpy = jasmine.createSpyObj('CatalogService', ['getCatalogDescriptors', 'retrieveCatalogDescriptors']); + catalogServiceSpy = jasmine.createSpyObj('CatalogService', ['getCatalogDescriptors', 'retrieveCatalogDescriptors', 'setCatalogDescriptors']); TestBed.configureTestingModule({ providers: [ @@ -27,16 +27,21 @@ describe('CatalogResolver', () => { }); it('should resolve catalog descriptors calling the service if not present in memory', () => { + const descriptors = [{} as CatalogDescriptor]; catalogServiceSpy.getCatalogDescriptors.and.returnValue([]); - catalogServiceSpy.retrieveCatalogDescriptors.and.returnValue(of([{} as CatalogDescriptor])); - service.resolve(); + catalogServiceSpy.retrieveCatalogDescriptors.and.returnValue(of(descriptors)); + + service.resolve().subscribe(); + expect(catalogServiceSpy.retrieveCatalogDescriptors).toHaveBeenCalled(); + expect(catalogServiceSpy.setCatalogDescriptors).toHaveBeenCalledWith(descriptors); }); it('should resolve catalog descriptors without calling the service if present in memory', () => { catalogServiceSpy.getCatalogDescriptors.and.returnValue([{} as CatalogDescriptor]); service.resolve(); expect(catalogServiceSpy.retrieveCatalogDescriptors).not.toHaveBeenCalled(); + expect(catalogServiceSpy.setCatalogDescriptors).not.toHaveBeenCalled(); }); it('should handle errors from retrieveCatalogDescriptors and return empty array', (done) => { @@ -52,6 +57,7 @@ describe('CatalogResolver', () => { service.resolve().subscribe(result => { expect(result).toEqual([]); expect(console.error).toHaveBeenCalledWith('Error retrieving catalog descriptors', error); + expect(catalogServiceSpy.setCatalogDescriptors).toHaveBeenCalledWith([]); done(); }); }); diff --git a/src/app/services/catalog-resolver.service.ts b/src/app/services/catalog-resolver.service.ts index f480c0b..cd2003b 100644 --- a/src/app/services/catalog-resolver.service.ts +++ b/src/app/services/catalog-resolver.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { Resolve } from '@angular/router'; -import { catchError, Observable, of } from 'rxjs'; +import { catchError, Observable, of, tap } from 'rxjs'; import { CatalogService } from './catalog.service'; import { CatalogDescriptor } from '../openapi/component-catalog'; @@ -14,10 +14,14 @@ export class CatalogResolver implements Resolve { const catalogDescriptors = this.catalogService.getCatalogDescriptors(); if (catalogDescriptors.length > 0) { return of(catalogDescriptors); - } + } return this.catalogService.retrieveCatalogDescriptors().pipe( + tap((descriptors) => { + this.catalogService.setCatalogDescriptors(descriptors); + }), catchError(error => { console.error('Error retrieving catalog descriptors', error); + this.catalogService.setCatalogDescriptors([]); return of([]); }) );