From 848a7121135d5dd41dc0d5229d63b718da6db58e Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Wed, 17 Jan 2024 23:16:30 +0100 Subject: [PATCH 01/31] fix: tests now run when script is used --- AdminUi/src/AdminUi/ClientApp/src/test.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 AdminUi/src/AdminUi/ClientApp/src/test.ts diff --git a/AdminUi/src/AdminUi/ClientApp/src/test.ts b/AdminUi/src/AdminUi/ClientApp/src/test.ts new file mode 100644 index 0000000000..a6aa1b98d2 --- /dev/null +++ b/AdminUi/src/AdminUi/ClientApp/src/test.ts @@ -0,0 +1,14 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import "zone.js/testing"; +import { getTestBed } from "@angular/core/testing"; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from "@angular/platform-browser-dynamic/testing"; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting(), +); From e0329fb864243251b735d1b3ba0317e8faa0e0a3 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Wed, 17 Jan 2024 23:17:47 +0100 Subject: [PATCH 02/31] chore: add data to support breadcrumb label --- .../ClientApp/src/app/app-routing.module.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts b/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts index 3db1d7d4ea..99ee9fdbe8 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts @@ -14,15 +14,15 @@ import { LoginComponent } from "./components/shared/login/login.component"; const routes: Routes = [ { path: "", redirectTo: "/dashboard", pathMatch: "full" }, { path: "login", component: LoginComponent }, - { path: "dashboard", component: DashboardComponent, canActivate: [AuthGuard] }, - { path: "identities", component: IdentityListComponent, canActivate: [AuthGuard] }, - { path: "identities/:address", component: IdentityDetailsComponent, canActivate: [AuthGuard] }, - { path: "tiers", component: TierListComponent, canActivate: [AuthGuard] }, - { path: "tiers/create", component: TierEditComponent, canActivate: [AuthGuard] }, - { path: "tiers/:id", component: TierEditComponent, canActivate: [AuthGuard] }, - { path: "clients", component: ClientListComponent, canActivate: [AuthGuard] }, - { path: "clients/create", component: ClientEditComponent, canActivate: [AuthGuard] }, - { path: "clients/:id", component: ClientEditComponent, canActivate: [AuthGuard] }, + { path: "dashboard", component: DashboardComponent, data:{ breadcrumb: "Dashboard" }, canActivate: [AuthGuard] }, + { path: "identities", component: IdentityListComponent, data:{ breadcrumb: "Identities" }, canActivate: [AuthGuard] }, + { path: "identities/:address", component: IdentityDetailsComponent, data:{ breadcrumb: "Edit Identity" }, canActivate: [AuthGuard] }, + { path: "tiers", component: TierListComponent, data:{ breadcrumb: "Tiers" }, canActivate: [AuthGuard] }, + { path: "tiers/create", component: TierEditComponent, data:{ breadcrumb: "Create Tier" }, canActivate: [AuthGuard] }, + { path: "tiers/:id", component: TierEditComponent, data:{ breadcrumb: "Edit Tier" },canActivate: [AuthGuard] }, + { path: "clients", component: ClientListComponent, data:{ breadcrumb: "Clients" }, canActivate: [AuthGuard] }, + { path: "clients/create", component: ClientEditComponent, data:{ breadcrumb: "Create Client" }, canActivate: [AuthGuard] }, + { path: "clients/:id", component: ClientEditComponent, data:{ breadcrumb: "Edit Client" }, canActivate: [AuthGuard] }, { path: "**", component: PageNotFoundComponent } ]; From 997e48365e6acfa6a7792b9a913c15b6107e9cf2 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Thu, 18 Jan 2024 12:28:39 +0100 Subject: [PATCH 03/31] feat: implement BreadcrumbService and add appropriate tests for BreadcrumbService --- .../breadcrumb.service.spec.ts | 164 ++++++++++++++++++ .../breadcrumb-service/breadcrumb.service.ts | 96 ++++++++++ 2 files changed, 260 insertions(+) create mode 100644 AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts create mode 100644 AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts new file mode 100644 index 0000000000..24292589b8 --- /dev/null +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts @@ -0,0 +1,164 @@ +import { TestBed } from "@angular/core/testing"; +import { BreadcrumbService } from "./breadcrumb.service"; +import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; +import { BehaviorSubject } from "rxjs"; + +class MockActivatedRoute { + private readonly subject = new BehaviorSubject(this.testParams); + public params = this.subject.asObservable(); + + private _testParams!: {}; + public get testParams() { + return this._testParams; + } + public set testParams(params: {}) { + this._testParams = params; + this.subject.next(params); + } +} + +describe("BreadcrumbService", function() { + let breadcrumbService: BreadcrumbService; + let mockRouter: Router; + let mockActivatedRoute: MockActivatedRoute; + + const initialHistory = [{label: "Home", url: "/home"}, {label: "Details", url: "/details"}, {label: "Products", url: "/products"}]; + + beforeEach(function() { + mockRouter = { + events: new BehaviorSubject(new NavigationEnd(0, "", "")) + } as any; + + mockActivatedRoute = new MockActivatedRoute(); + + TestBed.configureTestingModule({ + providers: [ + BreadcrumbService, + { provide: Router, useValue: mockRouter }, + { provide: ActivatedRoute, useValue: mockActivatedRoute } + ] + }); + breadcrumbService = TestBed.inject(BreadcrumbService); + }); + + it("should be created",async function() { + await expect(breadcrumbService).toBeTruthy(); + }); + + it("should return an empty array initially",async function() { + // Arrange + + // Act + const result = breadcrumbService.getBreadcrumbHistory(); + + // Assert + await expect(result.length).toEqual(0); + }); + + it("should return the breadcrumb history array",async function() { + // Arrange + const expectedHistory = [{label: "Home", url: "/home"}, {label: "Details", url: "/details"}, {label: "Products", url: "/products"}]; + + // Act + expectedHistory.forEach(trail => { + breadcrumbService["breadcrumbHistory"].push(trail); + }); + const result = breadcrumbService.getBreadcrumbHistory(); + + // Assert + await expect(result).toEqual(expectedHistory); + }); + + it("should return a flat array when multiple trails are added", async function() { + // Arrange + const firstTrail = [{ label: "Home", url: "/home" }]; + const secondTrail = [{ label: "Details", url: "/details" }, { label: "Products", url: "/products" }]; + + breadcrumbService["breadcrumbHistory"] = [...firstTrail, ...secondTrail]; + + // Act + const result = breadcrumbService.getBreadcrumbHistory(); + + // Assert + await expect(result).toEqual([...firstTrail, ...secondTrail]); + }); + + it("should return a copy of the breadcrumb history array", async function() { + // Arrange + const expectedHistory = [ + { label: "Home", url: "/home" }, + { label: "Details", url: "/details" }, + { label: "Products", url: "/products" } + ]; + + // Act + expectedHistory.forEach(trail => { + breadcrumbService["breadcrumbHistory"].push(trail); + }); + const result = breadcrumbService.getBreadcrumbHistory(); + + // Modify the result + result[0].label = "Modified"; + + // Assert + await expect(result).not.toEqual(expectedHistory); + }); + + it("should not modify the internal breadcrumb history array when modifying the result", async function() { + // Arrange + const expectedHistory = [ + { label: "Home", url: "/home" }, + { label: "Details", url: "/details" }, + { label: "Products", url: "/products" } + ]; + + // Act + expectedHistory.forEach(trail => { + breadcrumbService["breadcrumbHistory"].push(trail); + }); + const result = breadcrumbService.getBreadcrumbHistory(); + + // Modify the result + result[0].label = "Modified"; + + // Assert + await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(expectedHistory); + }); + + it("should do nothing if the index is negative", async function() { + // Arrange + breadcrumbService["breadcrumbHistory"] = [...initialHistory]; + + // Act + breadcrumbService.clearBreadcrumbHistoryAfterIndex(-1); + + // Assert + await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(initialHistory); + }); + + it("should do nothing if the index is equal to the last index", async function() { + // Arrange + breadcrumbService["breadcrumbHistory"] = [...initialHistory]; + const lastIndex = initialHistory.length - 1; + + // Act + breadcrumbService.clearBreadcrumbHistoryAfterIndex(lastIndex); + + // Assert + await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(initialHistory); + }); + + it("should clear history after the specified index", async function() { + // Arrange + const expectedHistory = [{label: "Home", url: "/home"}, {label: "Details", url: "/details"}]; + breadcrumbService["breadcrumbHistory"] = [...initialHistory]; + const indexToClear = 1; + + // Act + breadcrumbService.clearBreadcrumbHistoryAfterIndex(indexToClear); + + // Assert + await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(expectedHistory); + }); + +}); diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts new file mode 100644 index 0000000000..e5b83f67ef --- /dev/null +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts @@ -0,0 +1,96 @@ +import { Injectable } from "@angular/core"; +import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; +import { filter } from "rxjs/operators"; + +@Injectable({ + providedIn: "root" +}) +export class BreadcrumbService { + private breadcrumbHistory: Breadcrumb[] = []; + private readonly maxHistorySize = 10; + + public constructor(private readonly router: Router, private readonly activatedRoute: ActivatedRoute) { + const storedHistory = localStorage.getItem("breadcrumb-history"); + if (storedHistory) { + this.breadcrumbHistory = JSON.parse(storedHistory); + } + this.router.events + .pipe(filter((event) => event instanceof NavigationEnd)) + .subscribe(() => { + this.updateBreadcrumbHistory(); + localStorage.setItem("breadcrumb-history", JSON.stringify(this.breadcrumbHistory)); + }); + } + + public getBreadcrumbHistory(): Breadcrumb[] { + return [...this.breadcrumbHistory]; + } + + public clearBreadcrumbHistoryAfterIndex(index: number): void { + if (index >= 0 && index < this.breadcrumbHistory.length - 1) { + this.breadcrumbHistory.splice(index + 1); + } + } + + private updateBreadcrumbHistory(): void { + const breadcrumbs: Breadcrumb[] = []; + this.generateBreadcrumbs(this.activatedRoute.root, "", breadcrumbs); + + const breadcrumbTrail = [...breadcrumbs]; + + if (this.shouldClearBreadcrumbHistory(breadcrumbTrail)) { + this.breadcrumbHistory = []; + return; + } + + if (this.shouldPushBreadcrumbTrail(breadcrumbTrail)) { + this.breadcrumbHistory.push(...breadcrumbTrail); + } + + if (this.breadcrumbHistory.length > this.maxHistorySize) { + this.trimBreadcrumbHistory(); + } + } + + private shouldClearBreadcrumbHistory(trail: Breadcrumb[]): boolean { + return trail.some(breadcrumb => breadcrumb.url.includes("login")); + } + + private shouldPushBreadcrumbTrail(breadcrumbTrail: Breadcrumb[]): boolean { + return this.breadcrumbHistory.length === 0 || + this.breadcrumbHistory[this.breadcrumbHistory.length - 1].url !== breadcrumbTrail[breadcrumbTrail.length - 1].url; + } + + private trimBreadcrumbHistory(): void { + const currentSize = this.breadcrumbHistory.length; + if (currentSize > this.maxHistorySize) { + this.breadcrumbHistory = this.breadcrumbHistory.slice(currentSize - this.maxHistorySize); + } + } + + private generateBreadcrumbs(route: ActivatedRoute | null, url = "", breadcrumbs: Breadcrumb[] = []): void { + if (!route?.children) { + return; + } + + const children: ActivatedRoute[] = route.children; + + for (const child of children) { + const routeURL: string = child.snapshot.url.map((segment) => segment.path).join("/"); + + const breadcrumbLabel: string = child.snapshot.data.breadcrumb; + + if (routeURL !== "") { + url += `/${routeURL}`; + breadcrumbs.push({ label: breadcrumbLabel, url: url }); + } + + this.generateBreadcrumbs(child, url, breadcrumbs); + } + } +} + +export interface Breadcrumb { + label: string; + url: string; +} From 3920585b056f37d3be50073129489cd00c4f046e Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Thu, 18 Jan 2024 12:30:13 +0100 Subject: [PATCH 04/31] feat: add breadcrumb component to AdminUI --- .../breadcrumb/breadcrumb.component.css | 0 .../breadcrumb/breadcrumb.component.html | 10 +++++++ .../breadcrumb/breadcrumb.component.spec.ts | 23 +++++++++++++++ .../shared/breadcrumb/breadcrumb.component.ts | 28 +++++++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css create mode 100644 AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html create mode 100644 AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts create mode 100644 AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.ts diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html new file mode 100644 index 0000000000..4b7d1173f4 --- /dev/null +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts new file mode 100644 index 0000000000..678b81c85c --- /dev/null +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BreadcrumbComponent } from './breadcrumb.component'; + +describe('BreadcrumbComponent', () => { + let component: BreadcrumbComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BreadcrumbComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(BreadcrumbComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.ts new file mode 100644 index 0000000000..be61c124f4 --- /dev/null +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.ts @@ -0,0 +1,28 @@ +import { Component } from "@angular/core"; +import { NavigationEnd, Router } from "@angular/router"; +import { filter } from "rxjs/operators"; +import { Breadcrumb, BreadcrumbService } from "src/app/services/breadcrumb-service/breadcrumb.service"; + +@Component({ + selector: "app-breadcrumb", + templateUrl: "./breadcrumb.component.html", + styleUrls: ["./breadcrumb.component.css"] +}) +export class BreadcrumbComponent { + public breadcrumbHistory: Breadcrumb[] = []; + + public constructor(private readonly breadcrumbService: BreadcrumbService, private readonly router: Router) {} + + public ngOnInit(): void { + this.router.events.pipe( + filter(event => event instanceof NavigationEnd), + ).subscribe(() => { + this.breadcrumbHistory = this.breadcrumbService.getBreadcrumbHistory(); + }); + } + + public onBreadcrumbClick(index: number): void { + this.breadcrumbService.clearBreadcrumbHistoryAfterIndex(index); + this.breadcrumbHistory = this.breadcrumbService.getBreadcrumbHistory(); + } +} From c71a4761413b2f248e1651c4002940eb1703a136 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Thu, 18 Jan 2024 12:33:09 +0100 Subject: [PATCH 05/31] chore: add breadcrumb component to module and in appropriate place of view --- AdminUi/src/AdminUi/ClientApp/src/app/app.component.html | 1 + AdminUi/src/AdminUi/ClientApp/src/app/app.module.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/app.component.html b/AdminUi/src/AdminUi/ClientApp/src/app/app.component.html index 7f220bc662..367f404ae1 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/app.component.html +++ b/AdminUi/src/AdminUi/ClientApp/src/app/app.component.html @@ -6,6 +6,7 @@
+
diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/app.module.ts b/AdminUi/src/AdminUi/ClientApp/src/app/app.module.ts index 11d7ccba30..fb00ef3224 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/app.module.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/app.module.ts @@ -49,6 +49,7 @@ import { TierEditComponent } from "./components/quotas/tier/tier-edit/tier-edit. import { TierListComponent } from "./components/quotas/tier/tier-list/tier-list.component"; import { ConfirmationDialogComponent } from "./components/shared/confirmation-dialog/confirmation-dialog.component"; import { IdentitiesOverviewComponent } from "./components/shared/identities-overview/identities-overview.component"; +import { BreadcrumbComponent } from "./components/shared/breadcrumb/breadcrumb.component"; import { LoginComponent } from "./components/shared/login/login.component"; import { SidebarComponent } from "./components/sidebar/sidebar.component"; import { TopbarComponent } from "./components/topbar/topbar.component"; @@ -76,7 +77,8 @@ import { XSRFInterceptor } from "./shared/interceptors/xsrf.interceptor"; LoginComponent, ChangeSecretDialogComponent, IdentitiesOverviewComponent, - IdentityDetailsRelationshipsComponent + IdentityDetailsRelationshipsComponent, + BreadcrumbComponent ], imports: [ FormsModule, From e882a51de5b6d36cba0ab06705c5b2d471ce74c7 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Thu, 18 Jan 2024 12:33:44 +0100 Subject: [PATCH 06/31] chore: clear localStorage for breadcrumb history --- .../app/services/auth-service/auth.service.ts | 129 +++++++++--------- 1 file changed, 65 insertions(+), 64 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts index 59b0139000..e5c15a6838 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts @@ -1,64 +1,65 @@ -import { HttpClient } from "@angular/common/http"; -import { Injectable } from "@angular/core"; -import { Router } from "@angular/router"; -import { BehaviorSubject, Observable } from "rxjs"; -import { environment } from "src/environments/environment"; -import { XSRFService } from "../xsrf-service/xsrf.service"; - -@Injectable({ - providedIn: "root" -}) -export class AuthService { - private readonly loggedIn: BehaviorSubject = new BehaviorSubject(this.hasApiKey()); - private readonly apiUrl: string; - - public get isLoggedIn(): Observable { - return this.loggedIn.asObservable(); - } - - public constructor( - private readonly router: Router, - private readonly http: HttpClient, - private readonly xsrfService: XSRFService - ) { - this.apiUrl = environment.apiUrl; - } - - public isCurrentlyLoggedIn(): boolean { - return this.loggedIn.value; - } - - public hasApiKey(): boolean { - return !!localStorage.getItem("api-key"); - } - - public getApiKey(): string | null { - return localStorage.getItem("api-key"); - } - - public validateApiKey(apiKeyRequest: ValidateApiKeyRequest): Observable { - return this.http.post(`${this.apiUrl}/ValidateApiKey`, apiKeyRequest, { headers: { skip: "true" } }); - } - - public async login(apiKey: string): Promise { - localStorage.setItem("api-key", apiKey); - this.xsrfService.loadAndStoreXSRFToken(); - this.loggedIn.next(true); - await this.router.navigate(["/"]); - } - - public async logout(): Promise { - localStorage.removeItem("api-key"); - this.loggedIn.next(false); - this.xsrfService.clearStoredToken(); - return await this.router.navigate(["/login"]); - } -} - -export interface ValidateApiKeyResponse { - isValid: boolean; -} - -export interface ValidateApiKeyRequest { - apiKey: string; -} +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { Router } from "@angular/router"; +import { BehaviorSubject, Observable } from "rxjs"; +import { environment } from "src/environments/environment"; +import { XSRFService } from "../xsrf-service/xsrf.service"; + +@Injectable({ + providedIn: "root" +}) +export class AuthService { + private readonly loggedIn: BehaviorSubject = new BehaviorSubject(this.hasApiKey()); + private readonly apiUrl: string; + + public get isLoggedIn(): Observable { + return this.loggedIn.asObservable(); + } + + public constructor( + private readonly router: Router, + private readonly http: HttpClient, + private readonly xsrfService: XSRFService + ) { + this.apiUrl = environment.apiUrl; + } + + public isCurrentlyLoggedIn(): boolean { + return this.loggedIn.value; + } + + public hasApiKey(): boolean { + return !!localStorage.getItem("api-key"); + } + + public getApiKey(): string | null { + return localStorage.getItem("api-key"); + } + + public validateApiKey(apiKeyRequest: ValidateApiKeyRequest): Observable { + return this.http.post(`${this.apiUrl}/ValidateApiKey`, apiKeyRequest, { headers: { skip: "true" } }); + } + + public async login(apiKey: string): Promise { + localStorage.setItem("api-key", apiKey); + this.xsrfService.loadAndStoreXSRFToken(); + this.loggedIn.next(true); + await this.router.navigate(["/"]); + } + + public async logout(): Promise { + localStorage.removeItem("api-key"); + localStorage.removeItem("breadcrumb-history") + this.loggedIn.next(false); + this.xsrfService.clearStoredToken(); + return await this.router.navigate(["/login"]); + } +} + +export interface ValidateApiKeyResponse { + isValid: boolean; +} + +export interface ValidateApiKeyRequest { + apiKey: string; +} From bfbe5c753c863787bdc5adaf32dee15086522935 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Thu, 18 Jan 2024 17:06:50 +0100 Subject: [PATCH 07/31] fix: create a deep copy of the result --- .../services/breadcrumb-service/breadcrumb.service.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts index 24292589b8..a212d6788d 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts @@ -95,9 +95,7 @@ describe("BreadcrumbService", function() { expectedHistory.forEach(trail => { breadcrumbService["breadcrumbHistory"].push(trail); }); - const result = breadcrumbService.getBreadcrumbHistory(); - - // Modify the result + const result = JSON.parse(JSON.stringify(breadcrumbService.getBreadcrumbHistory())); result[0].label = "Modified"; // Assert From 614a68648f77efc0d733a127b26f013753a6081e Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Thu, 18 Jan 2024 17:15:44 +0100 Subject: [PATCH 08/31] chore: add css styling for breadcrumbs --- .../breadcrumb/breadcrumb.component.css | 29 +++++++++++++++++++ .../breadcrumb/breadcrumb.component.html | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css index e69de29bb2..808a188928 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css @@ -0,0 +1,29 @@ +nav { + font-size: 16px; + margin: 10px 0; + } + + a { + color: #17428d; + text-decoration: none; + transition: color 0.3s ease; + } + + a:hover { + color: #076fde; + } + + span::after { + content: " > "; + margin: 0 5px; + color: #999; + } + + span:last-child::after { + content: ""; + } + + span.active { + color: #333; + font-weight: bold; + } diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html index 4b7d1173f4..16268784cb 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html @@ -4,7 +4,7 @@ {{ trail.label }} {{ trail.label }} - > + > \ No newline at end of file From 0f091171812c1001033f5c3086a52840616cd005 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Fri, 19 Jan 2024 09:45:09 +0100 Subject: [PATCH 09/31] fix: eslint errors and add router module --- .../breadcrumb/breadcrumb.component.spec.ts | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts index 678b81c85c..f46df5dfa0 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts @@ -1,23 +1,24 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { BreadcrumbComponent } from './breadcrumb.component'; - -describe('BreadcrumbComponent', () => { - let component: BreadcrumbComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [BreadcrumbComponent] - }) - .compileComponents(); - - fixture = TestBed.createComponent(BreadcrumbComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { BreadcrumbComponent } from "./breadcrumb.component"; + +describe("BreadcrumbComponent", function() { + let component: BreadcrumbComponent; + let fixture: ComponentFixture; + + beforeEach(async function() { + await TestBed.configureTestingModule({ + declarations: [BreadcrumbComponent], + imports: [RouterTestingModule] + }) + .compileComponents(); + + fixture = TestBed.createComponent(BreadcrumbComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create",async function() { + await expect(component).toBeTruthy(); + }); +}); From 342bc591d9e610482b3960c183926cd46d2d36e2 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 22 Jan 2024 07:36:21 +0100 Subject: [PATCH 10/31] chore: click on one of the sidebar items resets the breadcrumbs --- .../breadcrumb-service/breadcrumb.service.ts | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts index e5b83f67ef..c8ee5537cf 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts @@ -43,13 +43,25 @@ export class BreadcrumbService { return; } - if (this.shouldPushBreadcrumbTrail(breadcrumbTrail)) { - this.breadcrumbHistory.push(...breadcrumbTrail); - } - if (this.breadcrumbHistory.length > this.maxHistorySize) { - this.trimBreadcrumbHistory(); + if (this.isMainLinkClicked(breadcrumbTrail)) { + this.breadcrumbHistory = breadcrumbTrail; + } else { + + if (this.shouldPushBreadcrumbTrail(breadcrumbTrail)) { + this.breadcrumbHistory.push(...breadcrumbTrail); + } + + if (this.breadcrumbHistory.length > this.maxHistorySize) { + this.trimBreadcrumbHistory(); + } } + + localStorage.setItem("breadcrumbHistory", JSON.stringify(this.breadcrumbHistory)); + } + + private isMainLinkClicked(breadcrumbTrail: Breadcrumb[]): boolean { + return breadcrumbTrail.length === 1 && breadcrumbTrail[0].url.split("/").length === 2; } private shouldClearBreadcrumbHistory(trail: Breadcrumb[]): boolean { From b3d39886a81851c249c45e2d9e2c922787e4c650 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 22 Jan 2024 08:33:05 +0100 Subject: [PATCH 11/31] chore: ignoring the history changes --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index da9223313f..43450a0471 100644 --- a/.gitignore +++ b/.gitignore @@ -337,6 +337,7 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ +**/.history/* # BeatPulse healthcheck temp database healthchecksdb From a03e141a4e65de245c5c2fc7898c3e01effdb901 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 22 Jan 2024 08:36:25 +0100 Subject: [PATCH 12/31] chore: prettier --- .../ClientApp/src/app/app-routing.module.ts | 18 +- .../breadcrumb/breadcrumb.component.css | 58 ++-- .../breadcrumb/breadcrumb.component.html | 19 +- .../breadcrumb/breadcrumb.component.spec.ts | 33 +- .../shared/breadcrumb/breadcrumb.component.ts | 33 +- .../app/services/auth-service/auth.service.ts | 130 ++++---- .../breadcrumb.service.spec.ts | 285 +++++++++--------- .../breadcrumb-service/breadcrumb.service.ts | 156 +++++----- AdminUi/src/AdminUi/ClientApp/src/test.ts | 22 +- 9 files changed, 377 insertions(+), 377 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts b/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts index 99ee9fdbe8..cbde90a6a0 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts @@ -14,15 +14,15 @@ import { LoginComponent } from "./components/shared/login/login.component"; const routes: Routes = [ { path: "", redirectTo: "/dashboard", pathMatch: "full" }, { path: "login", component: LoginComponent }, - { path: "dashboard", component: DashboardComponent, data:{ breadcrumb: "Dashboard" }, canActivate: [AuthGuard] }, - { path: "identities", component: IdentityListComponent, data:{ breadcrumb: "Identities" }, canActivate: [AuthGuard] }, - { path: "identities/:address", component: IdentityDetailsComponent, data:{ breadcrumb: "Edit Identity" }, canActivate: [AuthGuard] }, - { path: "tiers", component: TierListComponent, data:{ breadcrumb: "Tiers" }, canActivate: [AuthGuard] }, - { path: "tiers/create", component: TierEditComponent, data:{ breadcrumb: "Create Tier" }, canActivate: [AuthGuard] }, - { path: "tiers/:id", component: TierEditComponent, data:{ breadcrumb: "Edit Tier" },canActivate: [AuthGuard] }, - { path: "clients", component: ClientListComponent, data:{ breadcrumb: "Clients" }, canActivate: [AuthGuard] }, - { path: "clients/create", component: ClientEditComponent, data:{ breadcrumb: "Create Client" }, canActivate: [AuthGuard] }, - { path: "clients/:id", component: ClientEditComponent, data:{ breadcrumb: "Edit Client" }, canActivate: [AuthGuard] }, + { path: "dashboard", component: DashboardComponent, data: { breadcrumb: "Dashboard" }, canActivate: [AuthGuard] }, + { path: "identities", component: IdentityListComponent, data: { breadcrumb: "Identities" }, canActivate: [AuthGuard] }, + { path: "identities/:address", component: IdentityDetailsComponent, data: { breadcrumb: "Edit Identity" }, canActivate: [AuthGuard] }, + { path: "tiers", component: TierListComponent, data: { breadcrumb: "Tiers" }, canActivate: [AuthGuard] }, + { path: "tiers/create", component: TierEditComponent, data: { breadcrumb: "Create Tier" }, canActivate: [AuthGuard] }, + { path: "tiers/:id", component: TierEditComponent, data: { breadcrumb: "Edit Tier" }, canActivate: [AuthGuard] }, + { path: "clients", component: ClientListComponent, data: { breadcrumb: "Clients" }, canActivate: [AuthGuard] }, + { path: "clients/create", component: ClientEditComponent, data: { breadcrumb: "Create Client" }, canActivate: [AuthGuard] }, + { path: "clients/:id", component: ClientEditComponent, data: { breadcrumb: "Edit Client" }, canActivate: [AuthGuard] }, { path: "**", component: PageNotFoundComponent } ]; diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css index 808a188928..2346759fdb 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.css @@ -1,29 +1,29 @@ -nav { - font-size: 16px; - margin: 10px 0; - } - - a { - color: #17428d; - text-decoration: none; - transition: color 0.3s ease; - } - - a:hover { - color: #076fde; - } - - span::after { - content: " > "; - margin: 0 5px; - color: #999; - } - - span:last-child::after { - content: ""; - } - - span.active { - color: #333; - font-weight: bold; - } +nav { + font-size: 16px; + margin: 10px 0; +} + +a { + color: #17428d; + text-decoration: none; + transition: color 0.3s ease; +} + +a:hover { + color: #076fde; +} + +span::after { + content: " > "; + margin: 0 5px; + color: #999; +} + +span:last-child::after { + content: ""; +} + +span.active { + color: #333; + font-weight: bold; +} diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html index 16268784cb..ce3bd1f95f 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.html @@ -1,10 +1,9 @@ - - \ No newline at end of file + diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts index f46df5dfa0..77a32b16e2 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.spec.ts @@ -2,23 +2,22 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { BreadcrumbComponent } from "./breadcrumb.component"; -describe("BreadcrumbComponent", function() { - let component: BreadcrumbComponent; - let fixture: ComponentFixture; +describe("BreadcrumbComponent", function () { + let component: BreadcrumbComponent; + let fixture: ComponentFixture; - beforeEach(async function() { - await TestBed.configureTestingModule({ - declarations: [BreadcrumbComponent], - imports: [RouterTestingModule] - }) - .compileComponents(); - - fixture = TestBed.createComponent(BreadcrumbComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(async function () { + await TestBed.configureTestingModule({ + declarations: [BreadcrumbComponent], + imports: [RouterTestingModule] + }).compileComponents(); - it("should create",async function() { - await expect(component).toBeTruthy(); - }); + fixture = TestBed.createComponent(BreadcrumbComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", async function () { + await expect(component).toBeTruthy(); + }); }); diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.ts index be61c124f4..18e3951b1d 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/breadcrumb/breadcrumb.component.ts @@ -4,25 +4,26 @@ import { filter } from "rxjs/operators"; import { Breadcrumb, BreadcrumbService } from "src/app/services/breadcrumb-service/breadcrumb.service"; @Component({ - selector: "app-breadcrumb", - templateUrl: "./breadcrumb.component.html", - styleUrls: ["./breadcrumb.component.css"] + selector: "app-breadcrumb", + templateUrl: "./breadcrumb.component.html", + styleUrls: ["./breadcrumb.component.css"] }) export class BreadcrumbComponent { - public breadcrumbHistory: Breadcrumb[] = []; + public breadcrumbHistory: Breadcrumb[] = []; - public constructor(private readonly breadcrumbService: BreadcrumbService, private readonly router: Router) {} + public constructor( + private readonly breadcrumbService: BreadcrumbService, + private readonly router: Router + ) {} - public ngOnInit(): void { - this.router.events.pipe( - filter(event => event instanceof NavigationEnd), - ).subscribe(() => { - this.breadcrumbHistory = this.breadcrumbService.getBreadcrumbHistory(); - }); - } + public ngOnInit(): void { + this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => { + this.breadcrumbHistory = this.breadcrumbService.getBreadcrumbHistory(); + }); + } - public onBreadcrumbClick(index: number): void { - this.breadcrumbService.clearBreadcrumbHistoryAfterIndex(index); - this.breadcrumbHistory = this.breadcrumbService.getBreadcrumbHistory(); - } + public onBreadcrumbClick(index: number): void { + this.breadcrumbService.clearBreadcrumbHistoryAfterIndex(index); + this.breadcrumbHistory = this.breadcrumbService.getBreadcrumbHistory(); + } } diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts index e5c15a6838..7ce7a3979e 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts @@ -1,65 +1,65 @@ -import { HttpClient } from "@angular/common/http"; -import { Injectable } from "@angular/core"; -import { Router } from "@angular/router"; -import { BehaviorSubject, Observable } from "rxjs"; -import { environment } from "src/environments/environment"; -import { XSRFService } from "../xsrf-service/xsrf.service"; - -@Injectable({ - providedIn: "root" -}) -export class AuthService { - private readonly loggedIn: BehaviorSubject = new BehaviorSubject(this.hasApiKey()); - private readonly apiUrl: string; - - public get isLoggedIn(): Observable { - return this.loggedIn.asObservable(); - } - - public constructor( - private readonly router: Router, - private readonly http: HttpClient, - private readonly xsrfService: XSRFService - ) { - this.apiUrl = environment.apiUrl; - } - - public isCurrentlyLoggedIn(): boolean { - return this.loggedIn.value; - } - - public hasApiKey(): boolean { - return !!localStorage.getItem("api-key"); - } - - public getApiKey(): string | null { - return localStorage.getItem("api-key"); - } - - public validateApiKey(apiKeyRequest: ValidateApiKeyRequest): Observable { - return this.http.post(`${this.apiUrl}/ValidateApiKey`, apiKeyRequest, { headers: { skip: "true" } }); - } - - public async login(apiKey: string): Promise { - localStorage.setItem("api-key", apiKey); - this.xsrfService.loadAndStoreXSRFToken(); - this.loggedIn.next(true); - await this.router.navigate(["/"]); - } - - public async logout(): Promise { - localStorage.removeItem("api-key"); - localStorage.removeItem("breadcrumb-history") - this.loggedIn.next(false); - this.xsrfService.clearStoredToken(); - return await this.router.navigate(["/login"]); - } -} - -export interface ValidateApiKeyResponse { - isValid: boolean; -} - -export interface ValidateApiKeyRequest { - apiKey: string; -} +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { Router } from "@angular/router"; +import { BehaviorSubject, Observable } from "rxjs"; +import { environment } from "src/environments/environment"; +import { XSRFService } from "../xsrf-service/xsrf.service"; + +@Injectable({ + providedIn: "root" +}) +export class AuthService { + private readonly loggedIn: BehaviorSubject = new BehaviorSubject(this.hasApiKey()); + private readonly apiUrl: string; + + public get isLoggedIn(): Observable { + return this.loggedIn.asObservable(); + } + + public constructor( + private readonly router: Router, + private readonly http: HttpClient, + private readonly xsrfService: XSRFService + ) { + this.apiUrl = environment.apiUrl; + } + + public isCurrentlyLoggedIn(): boolean { + return this.loggedIn.value; + } + + public hasApiKey(): boolean { + return !!localStorage.getItem("api-key"); + } + + public getApiKey(): string | null { + return localStorage.getItem("api-key"); + } + + public validateApiKey(apiKeyRequest: ValidateApiKeyRequest): Observable { + return this.http.post(`${this.apiUrl}/ValidateApiKey`, apiKeyRequest, { headers: { skip: "true" } }); + } + + public async login(apiKey: string): Promise { + localStorage.setItem("api-key", apiKey); + this.xsrfService.loadAndStoreXSRFToken(); + this.loggedIn.next(true); + await this.router.navigate(["/"]); + } + + public async logout(): Promise { + localStorage.removeItem("api-key"); + localStorage.removeItem("breadcrumb-history"); + this.loggedIn.next(false); + this.xsrfService.clearStoredToken(); + return await this.router.navigate(["/login"]); + } +} + +export interface ValidateApiKeyResponse { + isValid: boolean; +} + +export interface ValidateApiKeyRequest { + apiKey: string; +} diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts index a212d6788d..b4a5f00670 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts @@ -4,159 +4,168 @@ import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; import { BehaviorSubject } from "rxjs"; class MockActivatedRoute { - private readonly subject = new BehaviorSubject(this.testParams); - public params = this.subject.asObservable(); - - private _testParams!: {}; - public get testParams() { - return this._testParams; - } - public set testParams(params: {}) { - this._testParams = params; - this.subject.next(params); - } + private readonly subject = new BehaviorSubject(this.testParams); + public params = this.subject.asObservable(); + + private _testParams!: {}; + public get testParams() { + return this._testParams; + } + public set testParams(params: {}) { + this._testParams = params; + this.subject.next(params); + } } -describe("BreadcrumbService", function() { - let breadcrumbService: BreadcrumbService; - let mockRouter: Router; - let mockActivatedRoute: MockActivatedRoute; +describe("BreadcrumbService", function () { + let breadcrumbService: BreadcrumbService; + let mockRouter: Router; + let mockActivatedRoute: MockActivatedRoute; - const initialHistory = [{label: "Home", url: "/home"}, {label: "Details", url: "/details"}, {label: "Products", url: "/products"}]; + const initialHistory = [ + { label: "Home", url: "/home" }, + { label: "Details", url: "/details" }, + { label: "Products", url: "/products" } + ]; + + beforeEach(function () { + mockRouter = { + events: new BehaviorSubject(new NavigationEnd(0, "", "")) + } as any; - beforeEach(function() { - mockRouter = { - events: new BehaviorSubject(new NavigationEnd(0, "", "")) - } as any; + mockActivatedRoute = new MockActivatedRoute(); - mockActivatedRoute = new MockActivatedRoute(); + TestBed.configureTestingModule({ + providers: [BreadcrumbService, { provide: Router, useValue: mockRouter }, { provide: ActivatedRoute, useValue: mockActivatedRoute }] + }); + breadcrumbService = TestBed.inject(BreadcrumbService); + }); - TestBed.configureTestingModule({ - providers: [ - BreadcrumbService, - { provide: Router, useValue: mockRouter }, - { provide: ActivatedRoute, useValue: mockActivatedRoute } - ] + it("should be created", async function () { + await expect(breadcrumbService).toBeTruthy(); }); - breadcrumbService = TestBed.inject(BreadcrumbService); - }); - it("should be created",async function() { - await expect(breadcrumbService).toBeTruthy(); - }); + it("should return an empty array initially", async function () { + // Arrange - it("should return an empty array initially",async function() { - // Arrange + // Act + const result = breadcrumbService.getBreadcrumbHistory(); - // Act - const result = breadcrumbService.getBreadcrumbHistory(); + // Assert + await expect(result.length).toEqual(0); + }); + + it("should return the breadcrumb history array", async function () { + // Arrange + const expectedHistory = [ + { label: "Home", url: "/home" }, + { label: "Details", url: "/details" }, + { label: "Products", url: "/products" } + ]; + + // Act + expectedHistory.forEach((trail) => { + breadcrumbService["breadcrumbHistory"].push(trail); + }); + const result = breadcrumbService.getBreadcrumbHistory(); + + // Assert + await expect(result).toEqual(expectedHistory); + }); - // Assert - await expect(result.length).toEqual(0); - }); + it("should return a flat array when multiple trails are added", async function () { + // Arrange + const firstTrail = [{ label: "Home", url: "/home" }]; + const secondTrail = [ + { label: "Details", url: "/details" }, + { label: "Products", url: "/products" } + ]; - it("should return the breadcrumb history array",async function() { - // Arrange - const expectedHistory = [{label: "Home", url: "/home"}, {label: "Details", url: "/details"}, {label: "Products", url: "/products"}]; + breadcrumbService["breadcrumbHistory"] = [...firstTrail, ...secondTrail]; - // Act - expectedHistory.forEach(trail => { - breadcrumbService["breadcrumbHistory"].push(trail); + // Act + const result = breadcrumbService.getBreadcrumbHistory(); + + // Assert + await expect(result).toEqual([...firstTrail, ...secondTrail]); }); - const result = breadcrumbService.getBreadcrumbHistory(); - - // Assert - await expect(result).toEqual(expectedHistory); - }); - - it("should return a flat array when multiple trails are added", async function() { - // Arrange - const firstTrail = [{ label: "Home", url: "/home" }]; - const secondTrail = [{ label: "Details", url: "/details" }, { label: "Products", url: "/products" }]; - - breadcrumbService["breadcrumbHistory"] = [...firstTrail, ...secondTrail]; - - // Act - const result = breadcrumbService.getBreadcrumbHistory(); - - // Assert - await expect(result).toEqual([...firstTrail, ...secondTrail]); - }); - - it("should return a copy of the breadcrumb history array", async function() { - // Arrange - const expectedHistory = [ - { label: "Home", url: "/home" }, - { label: "Details", url: "/details" }, - { label: "Products", url: "/products" } - ]; - - // Act - expectedHistory.forEach(trail => { - breadcrumbService["breadcrumbHistory"].push(trail); + + it("should return a copy of the breadcrumb history array", async function () { + // Arrange + const expectedHistory = [ + { label: "Home", url: "/home" }, + { label: "Details", url: "/details" }, + { label: "Products", url: "/products" } + ]; + + // Act + expectedHistory.forEach((trail) => { + breadcrumbService["breadcrumbHistory"].push(trail); + }); + const result = JSON.parse(JSON.stringify(breadcrumbService.getBreadcrumbHistory())); + result[0].label = "Modified"; + + // Assert + await expect(result).not.toEqual(expectedHistory); }); - const result = JSON.parse(JSON.stringify(breadcrumbService.getBreadcrumbHistory())); - result[0].label = "Modified"; - - // Assert - await expect(result).not.toEqual(expectedHistory); - }); - - it("should not modify the internal breadcrumb history array when modifying the result", async function() { - // Arrange - const expectedHistory = [ - { label: "Home", url: "/home" }, - { label: "Details", url: "/details" }, - { label: "Products", url: "/products" } - ]; - - // Act - expectedHistory.forEach(trail => { - breadcrumbService["breadcrumbHistory"].push(trail); + + it("should not modify the internal breadcrumb history array when modifying the result", async function () { + // Arrange + const expectedHistory = [ + { label: "Home", url: "/home" }, + { label: "Details", url: "/details" }, + { label: "Products", url: "/products" } + ]; + + // Act + expectedHistory.forEach((trail) => { + breadcrumbService["breadcrumbHistory"].push(trail); + }); + const result = breadcrumbService.getBreadcrumbHistory(); + + // Modify the result + result[0].label = "Modified"; + + // Assert + await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(expectedHistory); }); - const result = breadcrumbService.getBreadcrumbHistory(); - - // Modify the result - result[0].label = "Modified"; - - // Assert - await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(expectedHistory); - }); - - it("should do nothing if the index is negative", async function() { - // Arrange - breadcrumbService["breadcrumbHistory"] = [...initialHistory]; - - // Act - breadcrumbService.clearBreadcrumbHistoryAfterIndex(-1); - - // Assert - await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(initialHistory); - }); - - it("should do nothing if the index is equal to the last index", async function() { - // Arrange - breadcrumbService["breadcrumbHistory"] = [...initialHistory]; - const lastIndex = initialHistory.length - 1; - - // Act - breadcrumbService.clearBreadcrumbHistoryAfterIndex(lastIndex); - - // Assert - await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(initialHistory); - }); - - it("should clear history after the specified index", async function() { - // Arrange - const expectedHistory = [{label: "Home", url: "/home"}, {label: "Details", url: "/details"}]; - breadcrumbService["breadcrumbHistory"] = [...initialHistory]; - const indexToClear = 1; - - // Act - breadcrumbService.clearBreadcrumbHistoryAfterIndex(indexToClear); - - // Assert - await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(expectedHistory); - }); + it("should do nothing if the index is negative", async function () { + // Arrange + breadcrumbService["breadcrumbHistory"] = [...initialHistory]; + + // Act + breadcrumbService.clearBreadcrumbHistoryAfterIndex(-1); + + // Assert + await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(initialHistory); + }); + + it("should do nothing if the index is equal to the last index", async function () { + // Arrange + breadcrumbService["breadcrumbHistory"] = [...initialHistory]; + const lastIndex = initialHistory.length - 1; + + // Act + breadcrumbService.clearBreadcrumbHistoryAfterIndex(lastIndex); + + // Assert + await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(initialHistory); + }); + + it("should clear history after the specified index", async function () { + // Arrange + const expectedHistory = [ + { label: "Home", url: "/home" }, + { label: "Details", url: "/details" } + ]; + breadcrumbService["breadcrumbHistory"] = [...initialHistory]; + const indexToClear = 1; + + // Act + breadcrumbService.clearBreadcrumbHistoryAfterIndex(indexToClear); + + // Assert + await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(expectedHistory); + }); }); diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts index c8ee5537cf..eb05fabefc 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts @@ -3,106 +3,104 @@ import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; import { filter } from "rxjs/operators"; @Injectable({ - providedIn: "root" + providedIn: "root" }) export class BreadcrumbService { - private breadcrumbHistory: Breadcrumb[] = []; - private readonly maxHistorySize = 10; + private breadcrumbHistory: Breadcrumb[] = []; + private readonly maxHistorySize = 10; + + public constructor( + private readonly router: Router, + private readonly activatedRoute: ActivatedRoute + ) { + const storedHistory = localStorage.getItem("breadcrumb-history"); + if (storedHistory) { + this.breadcrumbHistory = JSON.parse(storedHistory); + } + this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => { + this.updateBreadcrumbHistory(); + localStorage.setItem("breadcrumb-history", JSON.stringify(this.breadcrumbHistory)); + }); + } - public constructor(private readonly router: Router, private readonly activatedRoute: ActivatedRoute) { - const storedHistory = localStorage.getItem("breadcrumb-history"); - if (storedHistory) { - this.breadcrumbHistory = JSON.parse(storedHistory); + public getBreadcrumbHistory(): Breadcrumb[] { + return [...this.breadcrumbHistory]; } - this.router.events - .pipe(filter((event) => event instanceof NavigationEnd)) - .subscribe(() => { - this.updateBreadcrumbHistory(); - localStorage.setItem("breadcrumb-history", JSON.stringify(this.breadcrumbHistory)); - }); - } - - public getBreadcrumbHistory(): Breadcrumb[] { - return [...this.breadcrumbHistory]; - } - - public clearBreadcrumbHistoryAfterIndex(index: number): void { - if (index >= 0 && index < this.breadcrumbHistory.length - 1) { - this.breadcrumbHistory.splice(index + 1); + + public clearBreadcrumbHistoryAfterIndex(index: number): void { + if (index >= 0 && index < this.breadcrumbHistory.length - 1) { + this.breadcrumbHistory.splice(index + 1); + } } - } - private updateBreadcrumbHistory(): void { - const breadcrumbs: Breadcrumb[] = []; - this.generateBreadcrumbs(this.activatedRoute.root, "", breadcrumbs); + private updateBreadcrumbHistory(): void { + const breadcrumbs: Breadcrumb[] = []; + this.generateBreadcrumbs(this.activatedRoute.root, "", breadcrumbs); - const breadcrumbTrail = [...breadcrumbs]; + const breadcrumbTrail = [...breadcrumbs]; - if (this.shouldClearBreadcrumbHistory(breadcrumbTrail)) { - this.breadcrumbHistory = []; - return; - } + if (this.shouldClearBreadcrumbHistory(breadcrumbTrail)) { + this.breadcrumbHistory = []; + return; + } + + if (this.isMainLinkClicked(breadcrumbTrail)) { + this.breadcrumbHistory = breadcrumbTrail; + } else { + if (this.shouldPushBreadcrumbTrail(breadcrumbTrail)) { + this.breadcrumbHistory.push(...breadcrumbTrail); + } + + if (this.breadcrumbHistory.length > this.maxHistorySize) { + this.trimBreadcrumbHistory(); + } + } + localStorage.setItem("breadcrumbHistory", JSON.stringify(this.breadcrumbHistory)); + } - if (this.isMainLinkClicked(breadcrumbTrail)) { - this.breadcrumbHistory = breadcrumbTrail; - } else { + private isMainLinkClicked(breadcrumbTrail: Breadcrumb[]): boolean { + return breadcrumbTrail.length === 1 && breadcrumbTrail[0].url.split("/").length === 2; + } - if (this.shouldPushBreadcrumbTrail(breadcrumbTrail)) { - this.breadcrumbHistory.push(...breadcrumbTrail); - } - - if (this.breadcrumbHistory.length > this.maxHistorySize) { - this.trimBreadcrumbHistory(); - } + private shouldClearBreadcrumbHistory(trail: Breadcrumb[]): boolean { + return trail.some((breadcrumb) => breadcrumb.url.includes("login")); } - - localStorage.setItem("breadcrumbHistory", JSON.stringify(this.breadcrumbHistory)); - } - - private isMainLinkClicked(breadcrumbTrail: Breadcrumb[]): boolean { - return breadcrumbTrail.length === 1 && breadcrumbTrail[0].url.split("/").length === 2; - } - - private shouldClearBreadcrumbHistory(trail: Breadcrumb[]): boolean { - return trail.some(breadcrumb => breadcrumb.url.includes("login")); - } - - private shouldPushBreadcrumbTrail(breadcrumbTrail: Breadcrumb[]): boolean { - return this.breadcrumbHistory.length === 0 || - this.breadcrumbHistory[this.breadcrumbHistory.length - 1].url !== breadcrumbTrail[breadcrumbTrail.length - 1].url; - } - - private trimBreadcrumbHistory(): void { - const currentSize = this.breadcrumbHistory.length; - if (currentSize > this.maxHistorySize) { - this.breadcrumbHistory = this.breadcrumbHistory.slice(currentSize - this.maxHistorySize); + + private shouldPushBreadcrumbTrail(breadcrumbTrail: Breadcrumb[]): boolean { + return this.breadcrumbHistory.length === 0 || this.breadcrumbHistory[this.breadcrumbHistory.length - 1].url !== breadcrumbTrail[breadcrumbTrail.length - 1].url; } - } - private generateBreadcrumbs(route: ActivatedRoute | null, url = "", breadcrumbs: Breadcrumb[] = []): void { - if (!route?.children) { - return; + private trimBreadcrumbHistory(): void { + const currentSize = this.breadcrumbHistory.length; + if (currentSize > this.maxHistorySize) { + this.breadcrumbHistory = this.breadcrumbHistory.slice(currentSize - this.maxHistorySize); + } } - const children: ActivatedRoute[] = route.children; + private generateBreadcrumbs(route: ActivatedRoute | null, url = "", breadcrumbs: Breadcrumb[] = []): void { + if (!route?.children) { + return; + } + + const children: ActivatedRoute[] = route.children; + + for (const child of children) { + const routeURL: string = child.snapshot.url.map((segment) => segment.path).join("/"); - for (const child of children) { - const routeURL: string = child.snapshot.url.map((segment) => segment.path).join("/"); - - const breadcrumbLabel: string = child.snapshot.data.breadcrumb; + const breadcrumbLabel: string = child.snapshot.data.breadcrumb; - if (routeURL !== "") { - url += `/${routeURL}`; - breadcrumbs.push({ label: breadcrumbLabel, url: url }); - } + if (routeURL !== "") { + url += `/${routeURL}`; + breadcrumbs.push({ label: breadcrumbLabel, url: url }); + } - this.generateBreadcrumbs(child, url, breadcrumbs); + this.generateBreadcrumbs(child, url, breadcrumbs); + } } - } } export interface Breadcrumb { - label: string; - url: string; + label: string; + url: string; } diff --git a/AdminUi/src/AdminUi/ClientApp/src/test.ts b/AdminUi/src/AdminUi/ClientApp/src/test.ts index a6aa1b98d2..0ddf26967e 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/test.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/test.ts @@ -1,14 +1,8 @@ -// This file is required by karma.conf.js and loads recursively all the .spec and framework files - -import "zone.js/testing"; -import { getTestBed } from "@angular/core/testing"; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from "@angular/platform-browser-dynamic/testing"; - -// First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting(), -); +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import "zone.js/testing"; +import { getTestBed } from "@angular/core/testing"; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing"; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); From 657e020f6eb9d4ea75d775bce740e7a267f7f7fe Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 22 Jan 2024 09:26:30 +0100 Subject: [PATCH 13/31] chore: unnecessary set of the breadcrumb history --- .../src/app/services/breadcrumb-service/breadcrumb.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts index eb05fabefc..abefac28b2 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts @@ -55,8 +55,6 @@ export class BreadcrumbService { this.trimBreadcrumbHistory(); } } - - localStorage.setItem("breadcrumbHistory", JSON.stringify(this.breadcrumbHistory)); } private isMainLinkClicked(breadcrumbTrail: Breadcrumb[]): boolean { From 50e1b758cc0934809f9614baa0c08f41acb607f0 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 22 Jan 2024 10:49:45 +0100 Subject: [PATCH 14/31] chore: display dynamic data if there is one instead of breadcrumb label --- .../breadcrumb-service/breadcrumb.service.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts index abefac28b2..dd1e49adb8 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts @@ -90,12 +90,22 @@ export class BreadcrumbService { if (routeURL !== "") { url += `/${routeURL}`; - breadcrumbs.push({ label: breadcrumbLabel, url: url }); - } + const dynamicData = this.extractDynamicData(routeURL); + + breadcrumbs.push({ + label: dynamicData !== "" ? dynamicData : breadcrumbLabel, + url: url + }); + } this.generateBreadcrumbs(child, url, breadcrumbs); } } + + private extractDynamicData(routeURL: string): string { + const segments = routeURL.split("/"); + return segments.length === 2 ? segments[1] : ""; + } } export interface Breadcrumb { From 3062df17c0b986113b8a9d97b9d11da65fcd35b3 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 22 Jan 2024 10:58:29 +0100 Subject: [PATCH 15/31] chore: prettier --- .../app/services/breadcrumb-service/breadcrumb.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts index dd1e49adb8..6e9966b8a4 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts @@ -93,10 +93,10 @@ export class BreadcrumbService { const dynamicData = this.extractDynamicData(routeURL); breadcrumbs.push({ - label: dynamicData !== "" ? dynamicData : breadcrumbLabel, - url: url + label: dynamicData !== "" ? dynamicData : breadcrumbLabel, + url: url }); - } + } this.generateBreadcrumbs(child, url, breadcrumbs); } @@ -105,7 +105,7 @@ export class BreadcrumbService { private extractDynamicData(routeURL: string): string { const segments = routeURL.split("/"); return segments.length === 2 ? segments[1] : ""; - } + } } export interface Breadcrumb { From b930f350e38e55706b652f70071781f02f540d11 Mon Sep 17 00:00:00 2001 From: Timo Notheisen <65653426+tnotheis@users.noreply.github.com> Date: Mon, 22 Jan 2024 12:38:46 +0100 Subject: [PATCH 16/31] Helm Chart: Update Consumer API, Admin UI and Admin CLI (#501) * feat: update adminui * feat: update consumerapi --- helm/charts/adminui/Chart.yaml | 2 +- helm/charts/consumerapi/Chart.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/charts/adminui/Chart.yaml b/helm/charts/adminui/Chart.yaml index 17f3fc56c6..bf2d397fb2 100644 --- a/helm/charts/adminui/Chart.yaml +++ b/helm/charts/adminui/Chart.yaml @@ -6,4 +6,4 @@ type: application version: "1.0.0" -appVersion: "v2.4.1" +appVersion: "v2.4.3" diff --git a/helm/charts/consumerapi/Chart.yaml b/helm/charts/consumerapi/Chart.yaml index bf456e9361..96d9e4d634 100644 --- a/helm/charts/consumerapi/Chart.yaml +++ b/helm/charts/consumerapi/Chart.yaml @@ -6,4 +6,4 @@ type: application version: "1.0.0" -appVersion: "v4.2.5" +appVersion: "v4.3.0" From dcd55aac9c4dde38d47931a7b17bd78a5a515082 Mon Sep 17 00:00:00 2001 From: Timo Notheisen <65653426+tnotheis@users.noreply.github.com> Date: Mon, 22 Jan 2024 12:38:46 +0100 Subject: [PATCH 17/31] Helm Chart: Update Consumer API, Admin UI and Admin CLI (#501) * feat: update adminui * feat: update consumerapi --- helm/charts/adminui/Chart.yaml | 2 +- helm/charts/consumerapi/Chart.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/charts/adminui/Chart.yaml b/helm/charts/adminui/Chart.yaml index 17f3fc56c6..bf2d397fb2 100644 --- a/helm/charts/adminui/Chart.yaml +++ b/helm/charts/adminui/Chart.yaml @@ -6,4 +6,4 @@ type: application version: "1.0.0" -appVersion: "v2.4.1" +appVersion: "v2.4.3" diff --git a/helm/charts/consumerapi/Chart.yaml b/helm/charts/consumerapi/Chart.yaml index bf456e9361..96d9e4d634 100644 --- a/helm/charts/consumerapi/Chart.yaml +++ b/helm/charts/consumerapi/Chart.yaml @@ -6,4 +6,4 @@ type: application version: "1.0.0" -appVersion: "v4.2.5" +appVersion: "v4.3.0" From 436e7feb8452d3f24dfb73fabd5a5767f0575a95 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 22 Jan 2024 17:15:42 +0100 Subject: [PATCH 18/31] chore: now using session storage --- .../ClientApp/src/app/services/auth-service/auth.service.ts | 2 +- .../src/app/services/breadcrumb-service/breadcrumb.service.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts index 7ce7a3979e..c3206d968c 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts @@ -49,7 +49,7 @@ export class AuthService { public async logout(): Promise { localStorage.removeItem("api-key"); - localStorage.removeItem("breadcrumb-history"); + sessionStorage.removeItem("breadcrumb-history"); this.loggedIn.next(false); this.xsrfService.clearStoredToken(); return await this.router.navigate(["/login"]); diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts index 6e9966b8a4..1a0f06ebdc 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts @@ -13,13 +13,13 @@ export class BreadcrumbService { private readonly router: Router, private readonly activatedRoute: ActivatedRoute ) { - const storedHistory = localStorage.getItem("breadcrumb-history"); + const storedHistory = sessionStorage.getItem("breadcrumb-history"); if (storedHistory) { this.breadcrumbHistory = JSON.parse(storedHistory); } this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => { this.updateBreadcrumbHistory(); - localStorage.setItem("breadcrumb-history", JSON.stringify(this.breadcrumbHistory)); + sessionStorage.setItem("breadcrumb-history", JSON.stringify(this.breadcrumbHistory)); }); } From 4f4e5c04b183cb705a3f713f07abcb5d0d9486c8 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 29 Jan 2024 07:45:26 +0100 Subject: [PATCH 19/31] ci: trigger pipelines From f518702b9401697ecc3139eef686a4ff4b881754 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 29 Jan 2024 13:20:30 +0100 Subject: [PATCH 20/31] chore: remove unused breadcrumb data --- AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts b/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts index cbde90a6a0..1fa1bdf92a 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/app-routing.module.ts @@ -16,13 +16,13 @@ const routes: Routes = [ { path: "login", component: LoginComponent }, { path: "dashboard", component: DashboardComponent, data: { breadcrumb: "Dashboard" }, canActivate: [AuthGuard] }, { path: "identities", component: IdentityListComponent, data: { breadcrumb: "Identities" }, canActivate: [AuthGuard] }, - { path: "identities/:address", component: IdentityDetailsComponent, data: { breadcrumb: "Edit Identity" }, canActivate: [AuthGuard] }, + { path: "identities/:address", component: IdentityDetailsComponent, canActivate: [AuthGuard] }, { path: "tiers", component: TierListComponent, data: { breadcrumb: "Tiers" }, canActivate: [AuthGuard] }, { path: "tiers/create", component: TierEditComponent, data: { breadcrumb: "Create Tier" }, canActivate: [AuthGuard] }, - { path: "tiers/:id", component: TierEditComponent, data: { breadcrumb: "Edit Tier" }, canActivate: [AuthGuard] }, + { path: "tiers/:id", component: TierEditComponent, canActivate: [AuthGuard] }, { path: "clients", component: ClientListComponent, data: { breadcrumb: "Clients" }, canActivate: [AuthGuard] }, { path: "clients/create", component: ClientEditComponent, data: { breadcrumb: "Create Client" }, canActivate: [AuthGuard] }, - { path: "clients/:id", component: ClientEditComponent, data: { breadcrumb: "Edit Client" }, canActivate: [AuthGuard] }, + { path: "clients/:id", component: ClientEditComponent, canActivate: [AuthGuard] }, { path: "**", component: PageNotFoundComponent } ]; From 1223c784b7bfc3594427414d8ae7148603e25489 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 29 Jan 2024 13:27:15 +0100 Subject: [PATCH 21/31] refactor: enhance logout in AuthService to clear breadcrumb history --- .../ClientApp/src/app/services/auth-service/auth.service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts index c3206d968c..8b75eb1dbe 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/auth-service/auth.service.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { BehaviorSubject, Observable } from "rxjs"; import { environment } from "src/environments/environment"; import { XSRFService } from "../xsrf-service/xsrf.service"; +import { BreadcrumbService } from "../breadcrumb-service/breadcrumb.service"; @Injectable({ providedIn: "root" @@ -19,7 +20,8 @@ export class AuthService { public constructor( private readonly router: Router, private readonly http: HttpClient, - private readonly xsrfService: XSRFService + private readonly xsrfService: XSRFService, + private readonly breadcrumbService: BreadcrumbService ) { this.apiUrl = environment.apiUrl; } @@ -49,7 +51,7 @@ export class AuthService { public async logout(): Promise { localStorage.removeItem("api-key"); - sessionStorage.removeItem("breadcrumb-history"); + this.breadcrumbService.clearBreadcrumbHistoryAfterIndex(0); this.loggedIn.next(false); this.xsrfService.clearStoredToken(); return await this.router.navigate(["/login"]); From c96aaad6ff3f382d829c9fa59e7ce1ca3e4731f1 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 29 Jan 2024 13:29:05 +0100 Subject: [PATCH 22/31] test: now use map instead of forEach --- .../breadcrumb-service/breadcrumb.service.spec.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts index b4a5f00670..c19c6680f5 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts @@ -64,9 +64,7 @@ describe("BreadcrumbService", function () { ]; // Act - expectedHistory.forEach((trail) => { - breadcrumbService["breadcrumbHistory"].push(trail); - }); + breadcrumbService["breadcrumbHistory"] = expectedHistory.map(trail => ({ ...trail })); const result = breadcrumbService.getBreadcrumbHistory(); // Assert @@ -99,9 +97,7 @@ describe("BreadcrumbService", function () { ]; // Act - expectedHistory.forEach((trail) => { - breadcrumbService["breadcrumbHistory"].push(trail); - }); + breadcrumbService["breadcrumbHistory"] = expectedHistory.map(trail => ({ ...trail })); const result = JSON.parse(JSON.stringify(breadcrumbService.getBreadcrumbHistory())); result[0].label = "Modified"; From 3802601f6cd3e9f02be7e12156e4b624282941cc Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 29 Jan 2024 13:38:23 +0100 Subject: [PATCH 23/31] refactor: unify variable names and remove redundant type definitions as well return array of breadcrumbs in getBreadcrumbHistory --- .../breadcrumb-service/breadcrumb.service.ts | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts index 1a0f06ebdc..1188fc16d0 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts @@ -34,10 +34,9 @@ export class BreadcrumbService { } private updateBreadcrumbHistory(): void { - const breadcrumbs: Breadcrumb[] = []; - this.generateBreadcrumbs(this.activatedRoute.root, "", breadcrumbs); + const breadcrumbHistory: Breadcrumb[] = this.generateBreadcrumbHistory(this.activatedRoute.root, ""); - const breadcrumbTrail = [...breadcrumbs]; + const breadcrumbTrail = [...breadcrumbHistory]; if (this.shouldClearBreadcrumbHistory(breadcrumbTrail)) { this.breadcrumbHistory = []; @@ -57,16 +56,16 @@ export class BreadcrumbService { } } - private isMainLinkClicked(breadcrumbTrail: Breadcrumb[]): boolean { - return breadcrumbTrail.length === 1 && breadcrumbTrail[0].url.split("/").length === 2; + private isMainLinkClicked(breadcrumbHistory: Breadcrumb[]): boolean { + return breadcrumbHistory.length === 1 && breadcrumbHistory[0].url.split("/").length === 2; } private shouldClearBreadcrumbHistory(trail: Breadcrumb[]): boolean { return trail.some((breadcrumb) => breadcrumb.url.includes("login")); } - private shouldPushBreadcrumbTrail(breadcrumbTrail: Breadcrumb[]): boolean { - return this.breadcrumbHistory.length === 0 || this.breadcrumbHistory[this.breadcrumbHistory.length - 1].url !== breadcrumbTrail[breadcrumbTrail.length - 1].url; + private shouldPushBreadcrumbTrail(breadcrumbHistory: Breadcrumb[]): boolean { + return this.breadcrumbHistory.length === 0 || this.breadcrumbHistory[this.breadcrumbHistory.length - 1].url !== breadcrumbHistory[breadcrumbHistory.length - 1].url; } private trimBreadcrumbHistory(): void { @@ -76,30 +75,33 @@ export class BreadcrumbService { } } - private generateBreadcrumbs(route: ActivatedRoute | null, url = "", breadcrumbs: Breadcrumb[] = []): void { + private generateBreadcrumbHistory(route: ActivatedRoute | null, url = ""): Breadcrumb[] { if (!route?.children) { - return; + return []; } + const breadcrumbHistory: Breadcrumb[] = []; - const children: ActivatedRoute[] = route.children; + const children = route.children; for (const child of children) { - const routeURL: string = child.snapshot.url.map((segment) => segment.path).join("/"); + const routeURL = child.snapshot.url.map((segment) => segment.path).join("/"); - const breadcrumbLabel: string = child.snapshot.data.breadcrumb; + const breadcrumbLabel = child.snapshot.data.breadcrumb; if (routeURL !== "") { url += `/${routeURL}`; const dynamicData = this.extractDynamicData(routeURL); - breadcrumbs.push({ + breadcrumbHistory.push({ label: dynamicData !== "" ? dynamicData : breadcrumbLabel, url: url }); } - this.generateBreadcrumbs(child, url, breadcrumbs); + breadcrumbHistory.push(...this.generateBreadcrumbHistory(child, url)); } + + return breadcrumbHistory; } private extractDynamicData(routeURL: string): string { From b6839629e7c9fc3a266db91432fa57eadaec4b0f Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Mon, 29 Jan 2024 13:40:48 +0100 Subject: [PATCH 24/31] chore: prettier --- .../services/breadcrumb-service/breadcrumb.service.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts index c19c6680f5..20eb12723c 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts @@ -64,7 +64,7 @@ describe("BreadcrumbService", function () { ]; // Act - breadcrumbService["breadcrumbHistory"] = expectedHistory.map(trail => ({ ...trail })); + breadcrumbService["breadcrumbHistory"] = expectedHistory.map((trail) => ({ ...trail })); const result = breadcrumbService.getBreadcrumbHistory(); // Assert @@ -97,7 +97,7 @@ describe("BreadcrumbService", function () { ]; // Act - breadcrumbService["breadcrumbHistory"] = expectedHistory.map(trail => ({ ...trail })); + breadcrumbService["breadcrumbHistory"] = expectedHistory.map((trail) => ({ ...trail })); const result = JSON.parse(JSON.stringify(breadcrumbService.getBreadcrumbHistory())); result[0].label = "Modified"; From e027c3eb2afc24a0ebfb53eff7bcd5cea87e2f1c Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Tue, 30 Jan 2024 07:30:40 +0100 Subject: [PATCH 25/31] chore: replace forEach and map with simple for loop --- .../breadcrumb-service/breadcrumb.service.spec.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts index 20eb12723c..e3eb3b79c4 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts @@ -64,7 +64,9 @@ describe("BreadcrumbService", function () { ]; // Act - breadcrumbService["breadcrumbHistory"] = expectedHistory.map((trail) => ({ ...trail })); + for (const e of expectedHistory) { + breadcrumbService["breadcrumbHistory"].push(e); + } const result = breadcrumbService.getBreadcrumbHistory(); // Assert @@ -97,7 +99,9 @@ describe("BreadcrumbService", function () { ]; // Act - breadcrumbService["breadcrumbHistory"] = expectedHistory.map((trail) => ({ ...trail })); + for (const e of expectedHistory) { + breadcrumbService["breadcrumbHistory"].push(e); + } const result = JSON.parse(JSON.stringify(breadcrumbService.getBreadcrumbHistory())); result[0].label = "Modified"; @@ -114,9 +118,9 @@ describe("BreadcrumbService", function () { ]; // Act - expectedHistory.forEach((trail) => { - breadcrumbService["breadcrumbHistory"].push(trail); - }); + for (const e of expectedHistory) { + breadcrumbService["breadcrumbHistory"].push(e); + } const result = breadcrumbService.getBreadcrumbHistory(); // Modify the result From 34be81b370d0bcbc47f70b8f3e7e039f0ed31ecd Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Tue, 6 Feb 2024 09:10:15 +0100 Subject: [PATCH 26/31] ci: trigger pipelines From 67edd6b6c3c138a5e7c41ad9ed4a3e593bac0918 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Wed, 7 Feb 2024 09:11:29 +0100 Subject: [PATCH 27/31] ci: trigger pipelines From 46239b6e3d8098598817626bee8e6726ff6aad1f Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Fri, 9 Feb 2024 11:18:34 +0100 Subject: [PATCH 28/31] chore: unnecessary type definition --- .../src/app/services/breadcrumb-service/breadcrumb.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts index 1188fc16d0..ae2bbeba3f 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts @@ -34,7 +34,7 @@ export class BreadcrumbService { } private updateBreadcrumbHistory(): void { - const breadcrumbHistory: Breadcrumb[] = this.generateBreadcrumbHistory(this.activatedRoute.root, ""); + const breadcrumbHistory = this.generateBreadcrumbHistory(this.activatedRoute.root, ""); const breadcrumbTrail = [...breadcrumbHistory]; From 228497fb639d2f0a63fa061a7bfef09455df48fa Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Fri, 9 Feb 2024 11:30:48 +0100 Subject: [PATCH 29/31] chore: remove null suppressing operator --- .../app/services/breadcrumb-service/breadcrumb.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts index e3eb3b79c4..4f94c4b5d5 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts @@ -7,7 +7,7 @@ class MockActivatedRoute { private readonly subject = new BehaviorSubject(this.testParams); public params = this.subject.asObservable(); - private _testParams!: {}; + private _testParams = {}; public get testParams() { return this._testParams; } From 6b0a459acf72f4bace449757eeb3fb1f3dd66da2 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Fri, 9 Feb 2024 11:32:05 +0100 Subject: [PATCH 30/31] chore: give a variable a proper name --- .../breadcrumb.service.spec.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts index 4f94c4b5d5..9b9a5104e7 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.spec.ts @@ -57,20 +57,20 @@ describe("BreadcrumbService", function () { it("should return the breadcrumb history array", async function () { // Arrange - const expectedHistory = [ + const expectedHistorySteps = [ { label: "Home", url: "/home" }, { label: "Details", url: "/details" }, { label: "Products", url: "/products" } ]; // Act - for (const e of expectedHistory) { - breadcrumbService["breadcrumbHistory"].push(e); + for (const historyStep of expectedHistorySteps) { + breadcrumbService["breadcrumbHistory"].push(historyStep); } const result = breadcrumbService.getBreadcrumbHistory(); // Assert - await expect(result).toEqual(expectedHistory); + await expect(result).toEqual(expectedHistorySteps); }); it("should return a flat array when multiple trails are added", async function () { @@ -92,34 +92,34 @@ describe("BreadcrumbService", function () { it("should return a copy of the breadcrumb history array", async function () { // Arrange - const expectedHistory = [ + const expectedHistorySteps = [ { label: "Home", url: "/home" }, { label: "Details", url: "/details" }, { label: "Products", url: "/products" } ]; // Act - for (const e of expectedHistory) { - breadcrumbService["breadcrumbHistory"].push(e); + for (const historyStep of expectedHistorySteps) { + breadcrumbService["breadcrumbHistory"].push(historyStep); } const result = JSON.parse(JSON.stringify(breadcrumbService.getBreadcrumbHistory())); result[0].label = "Modified"; // Assert - await expect(result).not.toEqual(expectedHistory); + await expect(result).not.toEqual(expectedHistorySteps); }); it("should not modify the internal breadcrumb history array when modifying the result", async function () { // Arrange - const expectedHistory = [ + const expectedHistorySteps = [ { label: "Home", url: "/home" }, { label: "Details", url: "/details" }, { label: "Products", url: "/products" } ]; // Act - for (const e of expectedHistory) { - breadcrumbService["breadcrumbHistory"].push(e); + for (const historyStep of expectedHistorySteps) { + breadcrumbService["breadcrumbHistory"].push(historyStep); } const result = breadcrumbService.getBreadcrumbHistory(); @@ -127,7 +127,7 @@ describe("BreadcrumbService", function () { result[0].label = "Modified"; // Assert - await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(expectedHistory); + await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(expectedHistorySteps); }); it("should do nothing if the index is negative", async function () { @@ -155,7 +155,7 @@ describe("BreadcrumbService", function () { it("should clear history after the specified index", async function () { // Arrange - const expectedHistory = [ + const expectedHistorySteps = [ { label: "Home", url: "/home" }, { label: "Details", url: "/details" } ]; @@ -166,6 +166,6 @@ describe("BreadcrumbService", function () { breadcrumbService.clearBreadcrumbHistoryAfterIndex(indexToClear); // Assert - await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(expectedHistory); + await expect(breadcrumbService.getBreadcrumbHistory()).toEqual(expectedHistorySteps); }); }); From 5e05dd56d1f5290c80ddc916b767e9b4d58b5cd2 Mon Sep 17 00:00:00 2001 From: Vladimir Vuckovic Date: Fri, 9 Feb 2024 15:02:23 +0100 Subject: [PATCH 31/31] chore: remove unnecessary array duplication --- .../breadcrumb-service/breadcrumb.service.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts index ae2bbeba3f..98defb28d4 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/services/breadcrumb-service/breadcrumb.service.ts @@ -36,18 +36,16 @@ export class BreadcrumbService { private updateBreadcrumbHistory(): void { const breadcrumbHistory = this.generateBreadcrumbHistory(this.activatedRoute.root, ""); - const breadcrumbTrail = [...breadcrumbHistory]; - - if (this.shouldClearBreadcrumbHistory(breadcrumbTrail)) { + if (this.shouldClearBreadcrumbHistory(breadcrumbHistory)) { this.breadcrumbHistory = []; return; } - if (this.isMainLinkClicked(breadcrumbTrail)) { - this.breadcrumbHistory = breadcrumbTrail; + if (this.isMainLinkClicked(breadcrumbHistory)) { + this.breadcrumbHistory = breadcrumbHistory; } else { - if (this.shouldPushBreadcrumbTrail(breadcrumbTrail)) { - this.breadcrumbHistory.push(...breadcrumbTrail); + if (this.shouldPushBreadcrumbHistory(breadcrumbHistory)) { + this.breadcrumbHistory.push(...breadcrumbHistory); } if (this.breadcrumbHistory.length > this.maxHistorySize) { @@ -64,7 +62,7 @@ export class BreadcrumbService { return trail.some((breadcrumb) => breadcrumb.url.includes("login")); } - private shouldPushBreadcrumbTrail(breadcrumbHistory: Breadcrumb[]): boolean { + private shouldPushBreadcrumbHistory(breadcrumbHistory: Breadcrumb[]): boolean { return this.breadcrumbHistory.length === 0 || this.breadcrumbHistory[this.breadcrumbHistory.length - 1].url !== breadcrumbHistory[breadcrumbHistory.length - 1].url; }