From e594cabe4a1b4aa26c0f3aa71d90512c34acb6d1 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Wed, 22 Dec 2021 18:01:37 +0100 Subject: [PATCH 1/6] [CST4981] finished task, working on unit testing --- .../admin-sidebar/admin-sidebar.component.ts | 18 ++-- .../collection-page-routing.module.ts | 1 + .../collection-page.component.ts | 16 +-- .../community-page-routing.module.ts | 1 + .../statistics-administrator.guard.ts | 28 +++++ .../data/feature-authorization/feature-id.ts | 1 + src/app/home-page/home-page-routing.module.ts | 1 + src/app/item-page/item-page-routing.module.ts | 1 + .../navbar-section.component.html | 5 +- src/app/navbar/navbar.component.html | 13 +-- src/app/navbar/navbar.component.ts | 13 ++- src/app/shared/menu/menu.component.spec.ts | 66 +++++++++++- src/app/shared/menu/menu.component.ts | 70 ++++++++++-- src/app/shared/menu/menu.effects.ts | 8 +- .../statistics-page-routing.module.ts | 101 +++++++++--------- src/test.ts | 2 +- .../dspace/app/navbar/navbar.component.html | 14 +-- 17 files changed, 256 insertions(+), 103 deletions(-) create mode 100644 src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.ts index f0d583744c..c81b2e6e93 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -21,6 +21,7 @@ import { MenuService } from '../../shared/menu/menu.service'; import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; +import { Router, ActivatedRoute } from '@angular/router'; /** * Component representing the admin sidebar @@ -63,14 +64,15 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { inFocus$: BehaviorSubject; constructor(protected menuService: MenuService, - protected injector: Injector, - private variableService: CSSVariableService, - private authService: AuthService, - private modalService: NgbModal, - private authorizationService: AuthorizationDataService, - private scriptDataService: ScriptDataService, + protected injector: Injector, + private variableService: CSSVariableService, + private authService: AuthService, + private modalService: NgbModal, + public authorizationService: AuthorizationDataService, + private scriptDataService: ScriptDataService, + public route: ActivatedRoute ) { - super(menuService, injector); + super(menuService, injector, authorizationService, route); this.inFocus$ = new BehaviorSubject(false); } @@ -144,7 +146,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { type: MenuItemType.TEXT, text: 'menu.section.new' } as TextMenuItemModel, - icon: 'plus', + icon: 'plus', index: 0 }, { diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts index 5879e523af..b92c5bf414 100644 --- a/src/app/collection-page/collection-page-routing.module.ts +++ b/src/app/collection-page/collection-page-routing.module.ts @@ -72,6 +72,7 @@ import { ThemedCollectionPageComponent } from './themed-collection-page.componen id: 'statistics_collection_:id', active: true, visible: true, + type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/collection-page/collection-page.component.ts b/src/app/collection-page/collection-page.component.ts index 366e1da7b1..62d072c249 100644 --- a/src/app/collection-page/collection-page.component.ts +++ b/src/app/collection-page/collection-page.component.ts @@ -103,20 +103,20 @@ export class CollectionPageComponent implements OnInit { const currentSort$ = this.paginationService.getCurrentSort(this.paginationConfig.id, this.sortConfig); this.itemRD$ = observableCombineLatest([currentPagination$, currentSort$]).pipe( - switchMap(([currentPagination, currentSort ]) => this.collectionRD$.pipe( + switchMap(([currentPagination, currentSort]) => this.collectionRD$.pipe( getFirstSucceededRemoteData(), map((rd) => rd.payload.id), switchMap((id: string) => { return this.searchService.search( - new PaginatedSearchOptions({ - scope: id, - pagination: currentPagination, - sort: currentSort, - dsoTypes: [DSpaceObjectType.ITEM] - })).pipe(toDSpaceObjectListRD()) as Observable>>; + new PaginatedSearchOptions({ + scope: id, + pagination: currentPagination, + sort: currentSort, + dsoTypes: [DSpaceObjectType.ITEM] + })).pipe(toDSpaceObjectListRD()) as Observable>>; }), startWith(undefined) // Make sure switching pages shows loading component - ) + ) ) ); diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index ad1b1fd2f2..1be5472010 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -55,6 +55,7 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component' id: 'statistics_community_:id', active: true, visible: true, + type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts new file mode 100644 index 0000000000..41ea8550a7 --- /dev/null +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { SingleFeatureAuthorizationGuard } from './single-feature-authorization.guard'; +import { AuthorizationDataService } from '../authorization-data.service'; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; +import { AuthService } from '../../../auth/auth.service'; +import { Observable, of as observableOf } from 'rxjs'; +import { FeatureID } from '../feature-id'; +import { tap } from 'rxjs/operators'; + +/** + * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group + * management rights + */ +@Injectable({ + providedIn: 'root' +}) +export class StatisticsAdministratorGuard extends SingleFeatureAuthorizationGuard { + constructor(protected authorizationService: AuthorizationDataService, protected router: Router, protected authService: AuthService) { + super(authorizationService, router, authService); + } + + /** + * Check group management rights + */ + getFeatureID(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return observableOf(FeatureID.CanViewUsageStatistics); + } +} diff --git a/src/app/core/data/feature-authorization/feature-id.ts b/src/app/core/data/feature-authorization/feature-id.ts index 15eba0e5db..b32292559e 100644 --- a/src/app/core/data/feature-authorization/feature-id.ts +++ b/src/app/core/data/feature-authorization/feature-id.ts @@ -25,4 +25,5 @@ export enum FeatureID { CanEditVersion = 'canEditVersion', CanDeleteVersion = 'canDeleteVersion', CanCreateVersion = 'canCreateVersion', + CanViewUsageStatistics = 'canViewUsageStatistics', } diff --git a/src/app/home-page/home-page-routing.module.ts b/src/app/home-page/home-page-routing.module.ts index 2a41c079da..2356170d4b 100644 --- a/src/app/home-page/home-page-routing.module.ts +++ b/src/app/home-page/home-page-routing.module.ts @@ -21,6 +21,7 @@ import { ThemedHomePageComponent } from './themed-home-page.component'; active: true, visible: true, index: 2, + type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index 7d7912bb42..b0412e5a0b 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -58,6 +58,7 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; id: 'statistics_item_:id', active: true, visible: true, + type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/navbar/navbar-section/navbar-section.component.html b/src/app/navbar/navbar-section/navbar-section.component.html index b5f6848050..d77b57515e 100644 --- a/src/app/navbar/navbar-section/navbar-section.component.html +++ b/src/app/navbar/navbar-section/navbar-section.component.html @@ -1,4 +1,3 @@ + + \ No newline at end of file diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index 3fa7598e74..fc5d1a2ef3 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -1,18 +1,15 @@ - \ No newline at end of file diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index e741cea285..df4dd72477 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -7,6 +7,11 @@ import { TextMenuItemModel } from '../shared/menu/menu-item/models/text.model'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { HostWindowService } from '../shared/host-window.service'; import { environment } from '../../environments/environment'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { Router, ActivatedRoute } from '@angular/router'; +import { map, take } from 'rxjs/operators'; +import { RemoteData } from '../core/data/remote-data'; +import { Collection } from 'src/app/core/shared/collection.model'; /** * Component representing the public navbar @@ -25,10 +30,12 @@ export class NavbarComponent extends MenuComponent { menuID = MenuID.PUBLIC; constructor(protected menuService: MenuService, - protected injector: Injector, - public windowService: HostWindowService + protected injector: Injector, + public windowService: HostWindowService, + public authorizationService: AuthorizationDataService, + public route: ActivatedRoute ) { - super(menuService, injector); + super(menuService, injector, authorizationService, route); } ngOnInit(): void { diff --git a/src/app/shared/menu/menu.component.spec.ts b/src/app/shared/menu/menu.component.spec.ts index 883969137b..14e28591f3 100644 --- a/src/app/shared/menu/menu.component.spec.ts +++ b/src/app/shared/menu/menu.component.spec.ts @@ -7,9 +7,12 @@ import { MenuComponent } from './menu.component'; import { MenuServiceStub } from '../testing/menu-service.stub'; import { of as observableOf } from 'rxjs'; import { MenuSection } from './menu.reducer'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { MenuID } from './initial-menus-state'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { createSuccessfulRemoteDataObject } from 'src/app/shared/remote-data.utils'; +import { Item } from '../../core/shared/item.model'; describe('MenuComponent', () => { let comp: MenuComponent; @@ -19,13 +22,38 @@ describe('MenuComponent', () => { const mockMenuID = 'mock-menuID' as MenuID; + const authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); + + const mockItem = Object.assign(new Item(), { + id: 'fake-id', + uuid: 'fake-id', + handle: 'fake/handle', + lastModified: '2018', + _links: { + self: { + href: 'https://localhost:8000/items/fake-id' + } + } + }); + + const routeStub = { + data: observableOf({ + dso: createSuccessfulRemoteDataObject(mockItem) + }), + children: [] + }; + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule], declarations: [MenuComponent], providers: [ Injector, - { provide: MenuService, useClass: MenuServiceStub } + { provide: MenuService, useClass: MenuServiceStub }, + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: ActivatedRoute, useValue: routeStub }, ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(MenuComponent, { @@ -95,4 +123,38 @@ describe('MenuComponent', () => { expect(menuService.collapseMenuPreview).toHaveBeenCalledWith(comp.menuID); })); }); + + describe('when unauthorized statistics', () => { + + beforeEach(() => { + comp.sections = observableOf([{ 'id': 'browse_global_communities_and_collections', 'active': false, 'visible': true, 'index': 0, 'model': { 'type': 1, 'text': 'menu.section.browse_global_communities_and_collections', 'link': '/community-list' }, 'shouldPersistOnRouteChange': true }, { 'id': 'browse_global', 'active': false, 'visible': true, 'index': 1, 'model': { 'type': 0, 'text': 'menu.section.browse_global' }, 'shouldPersistOnRouteChange': true }, { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }]); + authorizationService.isAuthorized().and.returnValue(observableOf(false)); + fixture.detectChanges(); + }); + + it('when authorized statistics', (done => { + comp.sections.subscribe((sections) => { + expect(sections.length).toEqual(2); + done(); + }); + })); + }); + + describe('get authorized statistics', () => { + + beforeEach(() => { + comp.sections = observableOf([{ 'id': 'browse_global_communities_and_collections', 'active': false, 'visible': true, 'index': 0, 'model': { 'type': 1, 'text': 'menu.section.browse_global_communities_and_collections', 'link': '/community-list' }, 'shouldPersistOnRouteChange': true }, { 'id': 'browse_global', 'active': false, 'visible': true, 'index': 1, 'model': { 'type': 0, 'text': 'menu.section.browse_global' }, 'shouldPersistOnRouteChange': true }, { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }]); + fixture.detectChanges(); + }); + + it('get authorized statistics', (done => { + comp.sections.subscribe((sections) => { + expect(sections.length).toEqual(3); + done(); + }); + })); + }); + + + }); diff --git a/src/app/shared/menu/menu.component.ts b/src/app/shared/menu/menu.component.ts index 32fd938f4e..1070521704 100644 --- a/src/app/shared/menu/menu.component.ts +++ b/src/app/shared/menu/menu.component.ts @@ -1,14 +1,17 @@ import { ChangeDetectionStrategy, Component, Injector, OnDestroy, OnInit } from '@angular/core'; -import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { BehaviorSubject, Observable, Subscription, of as observableOf } from 'rxjs'; import { MenuService } from './menu.service'; import { MenuID } from './initial-menus-state'; import { MenuSection } from './menu.reducer'; -import { distinctUntilChanged, map, switchMap } from 'rxjs/operators'; +import { distinctUntilChanged, map, switchMap, mergeMap, tap, isEmpty } from 'rxjs/operators'; import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { hasValue } from '../empty.util'; +import { hasValue, isNotEmpty, hasValueOperator, isNotEmptyOperator } from '../empty.util'; import { MenuSectionComponent } from './menu-section/menu-section.component'; import { getComponentForMenu } from './menu-section.decorator'; import { compareArraysUsingIds } from '../../item-page/simple/item-types/shared/item-relationships-utils'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; +import { Router, ActivatedRoute } from '@angular/router'; /** * A basic implementation of a MenuComponent @@ -67,27 +70,39 @@ export class MenuComponent implements OnInit, OnDestroy { */ subs: Subscription[] = []; - constructor(protected menuService: MenuService, protected injector: Injector) { + private activatedRouteLastChild: ActivatedRoute; + + constructor(protected menuService: MenuService, protected injector: Injector, public authorizationService: AuthorizationDataService, public route: ActivatedRoute) { } /** * Sets all instance variables to their initial values */ ngOnInit(): void { + this.activatedRouteLastChild = this.getActivatedRoute(this.route); this.menuCollapsed = this.menuService.isMenuCollapsed(this.menuID); this.menuPreviewCollapsed = this.menuService.isMenuPreviewCollapsed(this.menuID); this.menuVisible = this.menuService.isMenuVisible(this.menuID); this.sections = this.menuService.getMenuTopSections(this.menuID).pipe(distinctUntilChanged(compareArraysUsingIds())); + this.subs.push( this.sections.pipe( + tap(t => console.log(t)), // if you return an array from a switchMap it will emit each element as a separate event. // So this switchMap is equivalent to a subscribe with a forEach inside switchMap((sections: MenuSection[]) => sections), + switchMap((section: MenuSection) => { + if (section.id.includes('statistics')) { + return this.getAuthorizedStatistics(section); + } + return observableOf(section); + }), + isNotEmptyOperator(), switchMap((section: MenuSection) => this.getSectionComponent(section).pipe( - map((component: GenericConstructor) => ({ section, component })) + map((component: GenericConstructor) => ({ section, component })) )), - distinctUntilChanged((x,y) => x.section.id === y.section.id) - ).subscribe(({ section, component}) => { + distinctUntilChanged((x, y) => x.section.id === y.section.id) + ).subscribe(({ section, component }) => { const nextMap = this.sectionMap$.getValue(); nextMap.set(section.id, { injector: this.getSectionDataInjector(section), @@ -98,6 +113,43 @@ export class MenuComponent implements OnInit, OnDestroy { ); } + /** + * Get activated route of the deepest activated route + */ + getActivatedRoute(route) { + if (route.children.length > 0) { + return this.getActivatedRoute(route.firstChild); + } else { + return route; + } + } + + /** + * Get section of statistics after checking authorization + */ + getAuthorizedStatistics(section) { + return this.activatedRouteLastChild.data.pipe( + switchMap((data) => { + return this.authorizationService.isAuthorized(FeatureID.CanViewUsageStatistics, this.getObjectUrl(data)).pipe( + map((canViewUsageStatistics: boolean) => { + if (!canViewUsageStatistics) { + return {}; + } else { + return section; + } + })); + }) + ); + } + + /** + * Get statistics route dso data + */ + getObjectUrl(data) { + const object = data.site ? data.site : data.dso.payload; + return object._links.self.href; + } + /** * Collapse this menu when it's currently expanded, expand it when its currently collapsed * @param {Event} event The user event that triggered this method @@ -164,8 +216,8 @@ export class MenuComponent implements OnInit, OnDestroy { private getSectionComponent(section: MenuSection): Observable> { return this.menuService.hasSubSections(this.menuID, section.id).pipe( map((expandable: boolean) => { - return getComponentForMenu(this.menuID, expandable); - } + return getComponentForMenu(this.menuID, expandable); + } ), ); } diff --git a/src/app/shared/menu/menu.effects.ts b/src/app/shared/menu/menu.effects.ts index 47cff90209..5eafad7a98 100644 --- a/src/app/shared/menu/menu.effects.ts +++ b/src/app/shared/menu/menu.effects.ts @@ -19,7 +19,7 @@ export class MenuEffects { /** * On route change, build menu sections for every menu type depending on the current route data */ - @Effect({dispatch: false}) + @Effect({ dispatch: false }) public buildRouteMenuSections$: Observable = this.actions$ .pipe( ofType(ROUTER_NAVIGATED), @@ -31,8 +31,8 @@ export class MenuEffects { ); constructor(private actions$: Actions, - private menuService: MenuService, - private route: ActivatedRoute) { + private menuService: MenuService, + private route: ActivatedRoute) { } /** @@ -72,7 +72,6 @@ export class MenuEffects { const last: boolean = hasNoValue(route.firstChild); if (hasValue(data) && hasValue(data.menu) && hasValue(data.menu[menuID])) { - let menuSections: MenuSection[] | MenuSection = data.menu[menuID]; menuSections = this.resolveSubstitutions(menuSections, params); @@ -120,4 +119,5 @@ export class MenuEffects { } return resolved; } + } diff --git a/src/app/statistics-page/statistics-page-routing.module.ts b/src/app/statistics-page/statistics-page-routing.module.ts index 3c88e096e7..6047a82f1e 100644 --- a/src/app/statistics-page/statistics-page-routing.module.ts +++ b/src/app/statistics-page/statistics-page-routing.module.ts @@ -10,64 +10,69 @@ import { ThemedCommunityStatisticsPageComponent } from './community-statistics-p import { ThemedItemStatisticsPageComponent } from './item-statistics-page/themed-item-statistics-page.component'; import { ThemedSiteStatisticsPageComponent } from './site-statistics-page/themed-site-statistics-page.component'; import { ItemResolver } from '../item-page/item.resolver'; +import { StatisticsAdministratorGuard } from 'src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard'; @NgModule({ imports: [ StatisticsPageModule, RouterModule.forChild([ - { - path: '', - resolve: { - breadcrumb: I18nBreadcrumbResolver - }, - data: { - title: 'statistics.title', - breadcrumbKey: 'statistics' - }, - children: [ - { - path: '', - component: ThemedSiteStatisticsPageComponent, - }, - ] + { + path: '', + resolve: { + breadcrumb: I18nBreadcrumbResolver }, - { - path: `items/:id`, - resolve: { - scope: ItemResolver, - breadcrumb: I18nBreadcrumbResolver - }, - data: { - title: 'statistics.title', - breadcrumbKey: 'statistics' - }, - component: ThemedItemStatisticsPageComponent, + data: { + title: 'statistics.title', + breadcrumbKey: 'statistics' }, - { - path: `collections/:id`, - resolve: { - scope: CollectionPageResolver, - breadcrumb: I18nBreadcrumbResolver + children: [ + { + path: '', + component: ThemedSiteStatisticsPageComponent, }, - data: { - title: 'statistics.title', - breadcrumbKey: 'statistics' - }, - component: ThemedCollectionStatisticsPageComponent, + ], + canActivate: [StatisticsAdministratorGuard] + }, + { + path: `items/:id`, + resolve: { + scope: ItemResolver, + breadcrumb: I18nBreadcrumbResolver }, - { - path: `communities/:id`, - resolve: { - scope: CommunityPageResolver, - breadcrumb: I18nBreadcrumbResolver - }, - data: { - title: 'statistics.title', - breadcrumbKey: 'statistics' - }, - component: ThemedCommunityStatisticsPageComponent, + data: { + title: 'statistics.title', + breadcrumbKey: 'statistics' }, - ] + component: ThemedItemStatisticsPageComponent, + canActivate: [StatisticsAdministratorGuard] + }, + { + path: `collections/:id`, + resolve: { + scope: CollectionPageResolver, + breadcrumb: I18nBreadcrumbResolver + }, + data: { + title: 'statistics.title', + breadcrumbKey: 'statistics' + }, + component: ThemedCollectionStatisticsPageComponent, + canActivate: [StatisticsAdministratorGuard] + }, + { + path: `communities/:id`, + resolve: { + scope: CommunityPageResolver, + breadcrumb: I18nBreadcrumbResolver + }, + data: { + title: 'statistics.title', + breadcrumbKey: 'statistics' + }, + component: ThemedCommunityStatisticsPageComponent, + canActivate: [StatisticsAdministratorGuard] + }, + ] ) ], providers: [ diff --git a/src/test.ts b/src/test.ts index 16317897b1..c77b4bb2f0 100644 --- a/src/test.ts +++ b/src/test.ts @@ -15,6 +15,6 @@ getTestBed().initTestEnvironment( platformBrowserDynamicTesting() ); // Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); +const context = require.context('./app/shared/menu', true, /\.spec\.ts$/); // And load the modules. context.keys().map(context); diff --git a/src/themes/dspace/app/navbar/navbar.component.html b/src/themes/dspace/app/navbar/navbar.component.html index 5af30db632..f061c7cb3b 100644 --- a/src/themes/dspace/app/navbar/navbar.component.html +++ b/src/themes/dspace/app/navbar/navbar.component.html @@ -1,17 +1,14 @@ - \ No newline at end of file From 12ab877ae4965b8acc7102a05be0972dd07bf114 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Thu, 23 Dec 2021 21:59:24 +0100 Subject: [PATCH 2/6] [CST-4981] fixed and finished unit testing --- .../admin-sidebar.component.spec.ts | 43 +++++++++++++++---- .../community-authorizations.component.ts | 4 +- src/app/navbar/navbar.component.spec.ts | 35 ++++++++++++++- src/app/navbar/navbar.component.ts | 7 +-- src/app/shared/menu/menu.component.spec.ts | 36 ++++------------ src/app/shared/menu/menu.component.ts | 7 ++- .../resource-policy-form.component.spec.ts | 4 +- .../statistics-page-routing.module.ts | 2 +- src/test.ts | 2 +- 9 files changed, 87 insertions(+), 53 deletions(-) diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts index 948d7d86bc..65026c1504 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts @@ -18,6 +18,8 @@ import { ActivatedRoute } from '@angular/router'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import createSpy = jasmine.createSpy; +import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; +import { Item } from '../../core/shared/item.model'; describe('AdminSidebarComponent', () => { let comp: AdminSidebarComponent; @@ -26,6 +28,28 @@ describe('AdminSidebarComponent', () => { let authorizationService: AuthorizationDataService; let scriptService; + + const mockItem = Object.assign(new Item(), { + id: 'fake-id', + uuid: 'fake-id', + handle: 'fake/handle', + lastModified: '2018', + _links: { + self: { + href: 'https://localhost:8000/items/fake-id' + } + } + }); + + + const routeStub = { + data: observableOf({ + dso: createSuccessfulRemoteDataObject(mockItem) + }), + children: [] + }; + + beforeEach(waitForAsync(() => { authorizationService = jasmine.createSpyObj('authorizationService', { isAuthorized: observableOf(true) @@ -42,6 +66,7 @@ describe('AdminSidebarComponent', () => { { provide: ActivatedRoute, useValue: {} }, { provide: AuthorizationDataService, useValue: authorizationService }, { provide: ScriptDataService, useValue: scriptService }, + { provide: ActivatedRoute, useValue: routeStub }, { provide: NgbModal, useValue: { open: () => {/*comment*/ @@ -229,19 +254,19 @@ describe('AdminSidebarComponent', () => { it('should contain site admin section', () => { expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'admin_search', visible: true, + id: 'admin_search', visible: true, })); expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'registries', visible: true, + id: 'registries', visible: true, })); expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - parentID: 'registries', visible: true, + parentID: 'registries', visible: true, })); expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'curation_tasks', visible: true, + id: 'curation_tasks', visible: true, })); expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'workflow', visible: true, + id: 'workflow', visible: true, })); }); }); @@ -259,7 +284,7 @@ describe('AdminSidebarComponent', () => { it('should show edit_community', () => { expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'edit_community', visible: true, + id: 'edit_community', visible: true, })); }); }); @@ -277,7 +302,7 @@ describe('AdminSidebarComponent', () => { it('should show edit_collection', () => { expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'edit_collection', visible: true, + id: 'edit_collection', visible: true, })); }); }); @@ -295,10 +320,10 @@ describe('AdminSidebarComponent', () => { it('should show access control section', () => { expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - id: 'access_control', visible: true, + id: 'access_control', visible: true, })); expect(menuService.addSection).toHaveBeenCalledWith(comp.menuID, jasmine.objectContaining({ - parentID: 'access_control', visible: true, + parentID: 'access_control', visible: true, })); }); }); diff --git a/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts b/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts index 8b241af667..d2a95b1941 100644 --- a/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts +++ b/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts @@ -2,8 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; -import { RemoteData } from 'src/app/core/data/remote-data'; -import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; +import { RemoteData } from '../../../core/data/remote-data'; @Component({ selector: 'ds-community-authorizations', diff --git a/src/app/navbar/navbar.component.spec.ts b/src/app/navbar/navbar.component.spec.ts index cbe6738241..e8c9fae591 100644 --- a/src/app/navbar/navbar.component.spec.ts +++ b/src/app/navbar/navbar.component.spec.ts @@ -13,10 +13,39 @@ import { MenuService } from '../shared/menu/menu.service'; import { MenuServiceStub } from '../shared/testing/menu-service.stub'; import { ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; +import { Item } from '../core/shared/item.model'; +import { createSuccessfulRemoteDataObject } from '../shared/remote-data.utils'; +import { By } from '@angular/platform-browser'; +import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; let comp: NavbarComponent; let fixture: ComponentFixture; +const authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) +}); + +const mockItem = Object.assign(new Item(), { + id: 'fake-id', + uuid: 'fake-id', + handle: 'fake/handle', + lastModified: '2018', + _links: { + self: { + href: 'https://localhost:8000/items/fake-id' + } + } +}); + +const routeStub = { + data: observableOf({ + dso: createSuccessfulRemoteDataObject(mockItem) + }), + children: [] +}; + + + describe('NavbarComponent', () => { const menuService = new MenuServiceStub(); @@ -33,7 +62,8 @@ describe('NavbarComponent', () => { Injector, { provide: MenuService, useValue: menuService }, { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, - { provide: ActivatedRoute, useValue: {} } + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: ActivatedRoute, useValue: routeStub }, ], schemas: [NO_ERRORS_SCHEMA] }) @@ -42,7 +72,6 @@ describe('NavbarComponent', () => { // synchronous beforeEach beforeEach(() => { - spyOn(menuService, 'getMenuTopSections').and.returnValue(observableOf([])); fixture = TestBed.createComponent(NavbarComponent); @@ -53,4 +82,6 @@ describe('NavbarComponent', () => { it('should create', () => { expect(comp).toBeTruthy(); }); + + }); diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index df4dd72477..103449ff71 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -7,11 +7,8 @@ import { TextMenuItemModel } from '../shared/menu/menu-item/models/text.model'; import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { HostWindowService } from '../shared/host-window.service'; import { environment } from '../../environments/environment'; -import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; -import { Router, ActivatedRoute } from '@angular/router'; -import { map, take } from 'rxjs/operators'; -import { RemoteData } from '../core/data/remote-data'; -import { Collection } from 'src/app/core/shared/collection.model'; +import { ActivatedRoute } from '@angular/router'; +import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; /** * Component representing the public navbar diff --git a/src/app/shared/menu/menu.component.spec.ts b/src/app/shared/menu/menu.component.spec.ts index 14e28591f3..ce764d7757 100644 --- a/src/app/shared/menu/menu.component.spec.ts +++ b/src/app/shared/menu/menu.component.spec.ts @@ -10,9 +10,9 @@ import { MenuSection } from './menu.reducer'; import { Router, ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { MenuID } from './initial-menus-state'; -import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; -import { createSuccessfulRemoteDataObject } from 'src/app/shared/remote-data.utils'; import { Item } from '../../core/shared/item.model'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { createSuccessfulRemoteDataObject } from '../remote-data.utils'; describe('MenuComponent', () => { let comp: MenuComponent; @@ -22,6 +22,8 @@ describe('MenuComponent', () => { const mockMenuID = 'mock-menuID' as MenuID; + const mockStatisticSection = { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }; + const authorizationService = jasmine.createSpyObj('authorizationService', { isAuthorized: observableOf(true) }); @@ -38,6 +40,7 @@ describe('MenuComponent', () => { } }); + const routeStub = { data: observableOf({ dso: createSuccessfulRemoteDataObject(mockItem) @@ -125,36 +128,15 @@ describe('MenuComponent', () => { }); describe('when unauthorized statistics', () => { - - beforeEach(() => { - comp.sections = observableOf([{ 'id': 'browse_global_communities_and_collections', 'active': false, 'visible': true, 'index': 0, 'model': { 'type': 1, 'text': 'menu.section.browse_global_communities_and_collections', 'link': '/community-list' }, 'shouldPersistOnRouteChange': true }, { 'id': 'browse_global', 'active': false, 'visible': true, 'index': 1, 'model': { 'type': 0, 'text': 'menu.section.browse_global' }, 'shouldPersistOnRouteChange': true }, { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }]); - authorizationService.isAuthorized().and.returnValue(observableOf(false)); - fixture.detectChanges(); + it('should get observable of empty object', () => { + expect(comp.getAuthorizedStatistics(mockStatisticSection)).toBeObservable({}); }); - - it('when authorized statistics', (done => { - comp.sections.subscribe((sections) => { - expect(sections.length).toEqual(2); - done(); - }); - })); }); describe('get authorized statistics', () => { - - beforeEach(() => { - comp.sections = observableOf([{ 'id': 'browse_global_communities_and_collections', 'active': false, 'visible': true, 'index': 0, 'model': { 'type': 1, 'text': 'menu.section.browse_global_communities_and_collections', 'link': '/community-list' }, 'shouldPersistOnRouteChange': true }, { 'id': 'browse_global', 'active': false, 'visible': true, 'index': 1, 'model': { 'type': 0, 'text': 'menu.section.browse_global' }, 'shouldPersistOnRouteChange': true }, { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }]); - fixture.detectChanges(); + it('should get observable of empty object', () => { + expect(comp.getAuthorizedStatistics(mockStatisticSection)).toBeObservable(mockStatisticSection); }); - - it('get authorized statistics', (done => { - comp.sections.subscribe((sections) => { - expect(sections.length).toEqual(3); - done(); - }); - })); }); - - }); diff --git a/src/app/shared/menu/menu.component.ts b/src/app/shared/menu/menu.component.ts index 1070521704..584bee0b22 100644 --- a/src/app/shared/menu/menu.component.ts +++ b/src/app/shared/menu/menu.component.ts @@ -9,9 +9,9 @@ import { hasValue, isNotEmpty, hasValueOperator, isNotEmptyOperator } from '../e import { MenuSectionComponent } from './menu-section/menu-section.component'; import { getComponentForMenu } from './menu-section.decorator'; import { compareArraysUsingIds } from '../../item-page/simple/item-types/shared/item-relationships-utils'; -import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; -import { FeatureID } from 'src/app/core/data/feature-authorization/feature-id'; -import { Router, ActivatedRoute } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; +import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; +import { FeatureID } from '../../core/data/feature-authorization/feature-id'; /** * A basic implementation of a MenuComponent @@ -87,7 +87,6 @@ export class MenuComponent implements OnInit, OnDestroy { this.subs.push( this.sections.pipe( - tap(t => console.log(t)), // if you return an array from a switchMap it will emit each element as a separate event. // So this switchMap is equivalent to a subscribe with a forEach inside switchMap((sections: MenuSection[]) => sections), diff --git a/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts b/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts index 7f66eb052c..d65f7346a8 100644 --- a/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts +++ b/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts @@ -32,13 +32,13 @@ import { RESOURCE_POLICY } from '../../../core/resource-policy/models/resource-p import { EPersonMock } from '../../testing/eperson.mock'; import { isNotEmptyOperator } from '../../empty.util'; import { ActivatedRoute, Router } from '@angular/router'; -import { RemoteData } from 'src/app/core/data/remote-data'; import { RouterMock } from '../../mocks/router.mock'; import { Store } from '@ngrx/store'; import { PaginationServiceStub } from '../../testing/pagination-service.stub'; -import { PaginationService } from 'src/app/core/pagination/pagination.service'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { StoreMock } from '../../testing/store.mock'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PaginationService } from '../../../core/pagination/pagination.service'; export const mockResourcePolicyFormData = { name: [ diff --git a/src/app/statistics-page/statistics-page-routing.module.ts b/src/app/statistics-page/statistics-page-routing.module.ts index 6047a82f1e..ef6f68d557 100644 --- a/src/app/statistics-page/statistics-page-routing.module.ts +++ b/src/app/statistics-page/statistics-page-routing.module.ts @@ -10,7 +10,7 @@ import { ThemedCommunityStatisticsPageComponent } from './community-statistics-p import { ThemedItemStatisticsPageComponent } from './item-statistics-page/themed-item-statistics-page.component'; import { ThemedSiteStatisticsPageComponent } from './site-statistics-page/themed-site-statistics-page.component'; import { ItemResolver } from '../item-page/item.resolver'; -import { StatisticsAdministratorGuard } from 'src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard'; +import { StatisticsAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard'; @NgModule({ imports: [ diff --git a/src/test.ts b/src/test.ts index c77b4bb2f0..16317897b1 100644 --- a/src/test.ts +++ b/src/test.ts @@ -15,6 +15,6 @@ getTestBed().initTestEnvironment( platformBrowserDynamicTesting() ); // Then we find all the tests. -const context = require.context('./app/shared/menu', true, /\.spec\.ts$/); +const context = require.context('./', true, /\.spec\.ts$/); // And load the modules. context.keys().map(context); From f154fb60e09a19dd4500198c096a69a154371593 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Fri, 24 Dec 2021 17:49:55 +0100 Subject: [PATCH 3/6] [CST-4981] Fixed unit testing --- src/app/shared/menu/menu.component.spec.ts | 35 +++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/app/shared/menu/menu.component.spec.ts b/src/app/shared/menu/menu.component.spec.ts index ce764d7757..b84fad2b33 100644 --- a/src/app/shared/menu/menu.component.spec.ts +++ b/src/app/shared/menu/menu.component.spec.ts @@ -24,9 +24,7 @@ describe('MenuComponent', () => { const mockStatisticSection = { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }; - const authorizationService = jasmine.createSpyObj('authorizationService', { - isAuthorized: observableOf(true) - }); + let authorizationService: AuthorizationDataService; const mockItem = Object.assign(new Item(), { id: 'fake-id', @@ -49,6 +47,11 @@ describe('MenuComponent', () => { }; beforeEach(waitForAsync(() => { + + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(false) + }); + TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule], declarations: [MenuComponent], @@ -128,14 +131,32 @@ describe('MenuComponent', () => { }); describe('when unauthorized statistics', () => { - it('should get observable of empty object', () => { - expect(comp.getAuthorizedStatistics(mockStatisticSection)).toBeObservable({}); + + beforeEach(() => { + (authorizationService as any).isAuthorized.and.returnValue(observableOf(false)); + fixture.detectChanges(); + }); + + it('should return observable of empty object', done => { + comp.getAuthorizedStatistics(mockStatisticSection).subscribe((res) => { + expect(res).toEqual({}); + done(); + }); }); }); describe('get authorized statistics', () => { - it('should get observable of empty object', () => { - expect(comp.getAuthorizedStatistics(mockStatisticSection)).toBeObservable(mockStatisticSection); + + beforeEach(() => { + (authorizationService as any).isAuthorized.and.returnValue(observableOf(true)); + fixture.detectChanges(); + }); + + it('should return observable of statistics section menu', done => { + comp.getAuthorizedStatistics(mockStatisticSection).subscribe((res) => { + expect(res).toEqual(mockStatisticSection); + done(); + }); }); }); From 99aef984431ac35f98e054382e74913dd71bd640 Mon Sep 17 00:00:00 2001 From: Rezart Vata Date: Fri, 24 Dec 2021 19:34:10 +0100 Subject: [PATCH 4/6] [CST-4981] structure improvement,removed unecessary informations --- src/app/collection-page/collection-page-routing.module.ts | 1 - src/app/community-page/community-page-routing.module.ts | 1 - .../statistics-administrator.guard.ts | 1 - src/app/home-page/home-page-routing.module.ts | 1 - src/app/item-page/item-page-routing.module.ts | 1 - 5 files changed, 5 deletions(-) diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts index b92c5bf414..5879e523af 100644 --- a/src/app/collection-page/collection-page-routing.module.ts +++ b/src/app/collection-page/collection-page-routing.module.ts @@ -72,7 +72,6 @@ import { ThemedCollectionPageComponent } from './themed-collection-page.componen id: 'statistics_collection_:id', active: true, visible: true, - type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index 1be5472010..ad1b1fd2f2 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -55,7 +55,6 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component' id: 'statistics_community_:id', active: true, visible: true, - type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts index 41ea8550a7..680495686e 100644 --- a/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts +++ b/src/app/core/data/feature-authorization/feature-authorization-guard/statistics-administrator.guard.ts @@ -5,7 +5,6 @@ import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/ro import { AuthService } from '../../../auth/auth.service'; import { Observable, of as observableOf } from 'rxjs'; import { FeatureID } from '../feature-id'; -import { tap } from 'rxjs/operators'; /** * Prevent unauthorized activating and loading of routes when the current authenticated user doesn't have group diff --git a/src/app/home-page/home-page-routing.module.ts b/src/app/home-page/home-page-routing.module.ts index 2356170d4b..2a41c079da 100644 --- a/src/app/home-page/home-page-routing.module.ts +++ b/src/app/home-page/home-page-routing.module.ts @@ -21,7 +21,6 @@ import { ThemedHomePageComponent } from './themed-home-page.component'; active: true, visible: true, index: 2, - type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', diff --git a/src/app/item-page/item-page-routing.module.ts b/src/app/item-page/item-page-routing.module.ts index b0412e5a0b..7d7912bb42 100644 --- a/src/app/item-page/item-page-routing.module.ts +++ b/src/app/item-page/item-page-routing.module.ts @@ -58,7 +58,6 @@ import { REQUEST_COPY_MODULE_PATH } from '../app-routing-paths'; id: 'statistics_item_:id', active: true, visible: true, - type: 'statistics', model: { type: MenuItemType.LINK, text: 'menu.section.statistics', From 6d1674cc8a31d66f9d9ca9e8a41501c1be1e3353 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 12 Jan 2022 20:09:21 +0100 Subject: [PATCH 5/6] [CST-4981] Fix issue with missing statistics menu --- src/app/shared/menu/menu.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/shared/menu/menu.component.ts b/src/app/shared/menu/menu.component.ts index 584bee0b22..caf613a33f 100644 --- a/src/app/shared/menu/menu.component.ts +++ b/src/app/shared/menu/menu.component.ts @@ -1,11 +1,11 @@ import { ChangeDetectionStrategy, Component, Injector, OnDestroy, OnInit } from '@angular/core'; -import { BehaviorSubject, Observable, Subscription, of as observableOf } from 'rxjs'; +import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs'; import { MenuService } from './menu.service'; import { MenuID } from './initial-menus-state'; import { MenuSection } from './menu.reducer'; -import { distinctUntilChanged, map, switchMap, mergeMap, tap, isEmpty } from 'rxjs/operators'; +import { distinctUntilChanged, map, mergeMap, switchMap } from 'rxjs/operators'; import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { hasValue, isNotEmpty, hasValueOperator, isNotEmptyOperator } from '../empty.util'; +import { hasValue, isNotEmptyOperator } from '../empty.util'; import { MenuSectionComponent } from './menu-section/menu-section.component'; import { getComponentForMenu } from './menu-section.decorator'; import { compareArraysUsingIds } from '../../item-page/simple/item-types/shared/item-relationships-utils'; @@ -90,7 +90,7 @@ export class MenuComponent implements OnInit, OnDestroy { // if you return an array from a switchMap it will emit each element as a separate event. // So this switchMap is equivalent to a subscribe with a forEach inside switchMap((sections: MenuSection[]) => sections), - switchMap((section: MenuSection) => { + mergeMap((section: MenuSection) => { if (section.id.includes('statistics')) { return this.getAuthorizedStatistics(section); } From 0c9dc4286c43e8ac73a784a5803c2b1fcc5cb075 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 12 Jan 2022 20:09:48 +0100 Subject: [PATCH 6/6] [CST-4981] Fix error with yarn clean command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 403973ef98..278afdf6c3 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "clean:json": "rimraf *.records.json", "clean:node": "rimraf node_modules", "clean:prod": "yarn run clean:dist && yarn run clean:log && yarn run clean:doc && yarn run clean:coverage && yarn run clean:json", - "clean": "yarn run clean:prod && yarn run clean:node && yarn run clean:dev:config", + "clean": "yarn run clean:prod && yarn run clean:dev:config && yarn run clean:node", "sync-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts", "build:mirador": "webpack --config webpack/webpack.mirador.config.ts", "merge-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts",