From be6a6a2f62cccd34d641882840103302dd99879c Mon Sep 17 00:00:00 2001 From: Corrado Lombardi Date: Thu, 22 Oct 2020 15:50:44 +0200 Subject: [PATCH] [CSTPER-66] when a community or a collection is created or deleted, angular cache is refreshed so that in case of new community or collection it appears immediately in pages where communities and collections lists are displayed, in case of community or collection deletion deleted community or collection does not appear anymore in pages where communities and collections lists are displayed. --- .../create-comcol-page.component.spec.ts | 108 +++++++++++++++++- .../create-comcol-page.component.ts | 14 +-- .../delete-comcol-page.component.spec.ts | 107 ++++++++++++++++- .../delete-comcol-page.component.ts | 21 ++-- 4 files changed, 226 insertions(+), 24 deletions(-) diff --git a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts b/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts index 780589d0c5..d2335f623f 100644 --- a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts +++ b/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts @@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CommunityDataService } from '../../../core/data/community-data.service'; import { RouteService } from '../../../core/services/route.service'; import { Router } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; +import {TranslateModule, TranslateService} from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; import { Community } from '../../../core/shared/community.model'; import { SharedModule } from '../../shared.module'; @@ -12,12 +12,14 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { CreateComColPageComponent } from './create-comcol-page.component'; import { - createFailedRemoteDataObject$, + createFailedRemoteDataObject$, createNoContentRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; import { ComColDataService } from '../../../core/data/comcol-data.service'; import { NotificationsService } from '../../notifications/notifications.service'; import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; +import { RequestService } from '../../../core/data/request.service'; +import {getTestScheduler} from 'jasmine-marbles'; describe('CreateComColPageComponent', () => { let comp: CreateComColPageComponent; @@ -29,9 +31,11 @@ describe('CreateComColPageComponent', () => { let community; let newCommunity; + let parentCommunity; let communityDataServiceStub; let routeServiceStub; let routerStub; + let requestServiceStub; const logoEndpoint = 'rest/api/logo/endpoint'; @@ -44,6 +48,15 @@ describe('CreateComColPageComponent', () => { }] }); + parentCommunity = Object.assign(new Community(), { + uuid: 'a20da287-e174-466a-9926-f66as300d399', + id: 'a20da287-e174-466a-9926-f66as300d399', + metadata: [{ + key: 'dc.title', + value: 'parent community' + }] + }); + newCommunity = Object.assign(new Community(), { uuid: '1ff59938-a69a-4e62-b9a4-718569c55d48', metadata: [{ @@ -61,7 +74,8 @@ describe('CreateComColPageComponent', () => { }] })), create: (com, uuid?) => createSuccessfulRemoteDataObject$(newCommunity), - getLogoEndpoint: () => observableOf(logoEndpoint) + getLogoEndpoint: () => observableOf(logoEndpoint), + findByHref: () => null }; routeServiceStub = { @@ -71,6 +85,10 @@ describe('CreateComColPageComponent', () => { navigate: (commands) => commands }; + requestServiceStub = jasmine.createSpyObj('RequestService', { + removeByHrefSubstring: jasmine.createSpy('removeByHrefSubstring'), + }); + } beforeEach(async(() => { @@ -82,7 +100,9 @@ describe('CreateComColPageComponent', () => { { provide: CommunityDataService, useValue: communityDataServiceStub }, { provide: RouteService, useValue: routeServiceStub }, { provide: Router, useValue: routerStub }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() } + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: TranslateService, useValue: {}}, + { provide: RequestService, useValue: requestServiceStub} ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -123,19 +143,23 @@ describe('CreateComColPageComponent', () => { }; }); - it('should navigate when successful', () => { + it('should navigate and refresh cache when successful', () => { spyOn(router, 'navigate'); + spyOn((comp as any), 'refreshCache'); comp.onSubmit(data); fixture.detectChanges(); expect(router.navigate).toHaveBeenCalled(); + expect((comp as any).refreshCache).toHaveBeenCalled(); }); - it('should not navigate on failure', () => { + it('should neither navigate nor refresh cache on failure', () => { spyOn(router, 'navigate'); spyOn(dsoDataService, 'create').and.returnValue(createFailedRemoteDataObject$(newCommunity)); + spyOn((comp as any), 'refreshCache') comp.onSubmit(data); fixture.detectChanges(); expect(router.navigate).not.toHaveBeenCalled(); + expect((comp as any).refreshCache).not.toHaveBeenCalled(); }); }); @@ -182,5 +206,77 @@ describe('CreateComColPageComponent', () => { expect(data.uploader.uploadAll).toHaveBeenCalled(); }); }); + + describe('cache refresh', () => { + let scheduler; + let communityWithoutParentHref; + + beforeEach(() => { + scheduler = getTestScheduler(); + + }) + describe('cache refreshed top level community', () => { + beforeEach(() => { + spyOn(dsoDataService, 'findByHref').and.returnValue(createNoContentRemoteDataObject$()); + data = { + dso: Object.assign(new Community(), { + metadata: [{ + key: 'dc.title', + value: 'top level community' + }] + }), + _links: { + parentCommunity: { + href: 'topLevel/parentCommunity' + } + } + }; + communityWithoutParentHref = { + dso: Object.assign(new Community(), { + metadata: [{ + key: 'dc.title', + value: 'top level community' + }] + }), + _links: {} + }; + }); + it('top level community cache refreshed', () => { + scheduler.schedule(() => (comp as any).refreshCache(data)); + scheduler.flush(); + expect(requestServiceStub.removeByHrefSubstring).toHaveBeenCalledWith('communities/search/top'); + }); + it('top level community without parent link, cache not refreshed', () => { + scheduler.schedule(() => (comp as any).refreshCache(communityWithoutParentHref)); + scheduler.flush(); + expect(requestServiceStub.removeByHrefSubstring).not.toHaveBeenCalled(); + }); + }); + + describe('cache refreshed child community', () => { + beforeEach(() => { + spyOn(dsoDataService, 'findByHref').and.returnValue(createSuccessfulRemoteDataObject$(parentCommunity)); + data = { + dso: Object.assign(new Community(), { + metadata: [{ + key: 'dc.title', + value: 'child community' + }] + }), + _links: { + parentCommunity: { + href: 'child/parentCommunity' + } + } + }; + }); + it('child level community cache refreshed', () => { + scheduler.schedule(() => (comp as any).refreshCache(data)); + scheduler.flush(); + expect(requestServiceStub.removeByHrefSubstring).toHaveBeenCalledWith('a20da287-e174-466a-9926-f66as300d399'); + }); + }); + }); + }); }); diff --git a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts b/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts index f55a7f0156..336998aab3 100644 --- a/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts +++ b/src/app/shared/comcol-forms/create-comcol-page/create-comcol-page.component.ts @@ -11,8 +11,7 @@ import { Community } from '../../../core/shared/community.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { getFirstSucceededRemoteDataPayload, - getRemoteDataPayload, - getSucceededRemoteData + getSucceededOrNoContentResponse, } from '../../../core/shared/operators'; import { ResourceType } from '../../../core/shared/resource-type'; import {hasValue, isEmpty, isNotEmpty, isNotUndefined} from '../../empty.util'; @@ -123,11 +122,12 @@ export class CreateComColPageComponent implements return; } this.dsoDataService.findByHref(parentCommunityUrl).pipe( - getSucceededRemoteData(), - getRemoteDataPayload(), - map((pc: TDomain) => isEmpty(pc) ? 'communities/search/top' : pc.id), - take(1) - ).subscribe((href: string) => this.requestService.removeByHrefSubstring(href)); + getSucceededOrNoContentResponse(), + take(1), + ).subscribe((rd: RemoteData) => { + const href = rd.hasSucceeded && !isEmpty(rd.payload.id) ? rd.payload.id : 'communities/search/top'; + this.requestService.removeByHrefSubstring(href) + }); } private parentCommunityUrl(dso: Collection | Community) { diff --git a/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts b/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts index e791f41d56..9da77df007 100644 --- a/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts +++ b/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts @@ -1,7 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CommunityDataService } from '../../../core/data/community-data.service'; import { ActivatedRoute, Router } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; +import {TranslateModule, TranslateService} from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; import { Community } from '../../../core/shared/community.model'; import { SharedModule } from '../../shared.module'; @@ -13,6 +13,9 @@ import { DataService } from '../../../core/data/data.service'; import { DeleteComColPageComponent } from './delete-comcol-page.component'; import { NotificationsService } from '../../notifications/notifications.service'; import { NotificationsServiceStub } from '../../testing/notifications-service.stub'; +import {RequestService} from '../../../core/data/request.service'; +import {getTestScheduler} from 'jasmine-marbles'; +import {createNoContentRemoteDataObject$, createSuccessfulRemoteDataObject$} from '../../remote-data.utils'; describe('DeleteComColPageComponent', () => { let comp: DeleteComColPageComponent; @@ -22,9 +25,13 @@ describe('DeleteComColPageComponent', () => { let community; let newCommunity; + let parentCommunity; let routerStub; let routeStub; let notificationsService; + let translateServiceStub; + let requestServiceStub; + const validUUID = 'valid-uuid'; const invalidUUID = 'invalid-uuid'; const frontendURL = '/testType'; @@ -45,10 +52,20 @@ describe('DeleteComColPageComponent', () => { }] }); + parentCommunity = Object.assign(new Community(), { + uuid: 'a20da287-e174-466a-9926-f66as300d399', + id: 'a20da287-e174-466a-9926-f66as300d399', + metadata: [{ + key: 'dc.title', + value: 'parent community' + }] + }); + dsoDataService = jasmine.createSpyObj( 'dsoDataService', { - delete: observableOf({ isSuccessful: true }) + delete: observableOf({ isSuccessful: true }), + findByHref: jasmine.createSpy('findByHref') }); routerStub = { @@ -59,6 +76,14 @@ describe('DeleteComColPageComponent', () => { data: observableOf(community) }; + requestServiceStub = jasmine.createSpyObj('RequestService', { + removeByHrefSubstring: jasmine.createSpy('removeByHrefSubstring') + }); + + translateServiceStub = jasmine.createSpyObj('TranslateService', { + instant: jasmine.createSpy('instant') + }); + } beforeEach(async(() => { @@ -70,6 +95,8 @@ describe('DeleteComColPageComponent', () => { { provide: Router, useValue: routerStub }, { provide: ActivatedRoute, useValue: routeStub }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: TranslateService, useValue: translateServiceStub}, + { provide: RequestService, useValue: requestServiceStub} ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -108,17 +135,21 @@ describe('DeleteComColPageComponent', () => { it('should show an error notification on failure', () => { (dsoDataService.delete as any).and.returnValue(observableOf({ isSuccessful: false })); spyOn(router, 'navigate'); + spyOn((comp as any), 'refreshCache'); comp.onConfirm(data2); fixture.detectChanges(); expect(notificationsService.error).toHaveBeenCalled(); + expect((comp as any).refreshCache).not.toHaveBeenCalled(); expect(router.navigate).toHaveBeenCalled(); }); it('should show a success notification on success and navigate', () => { spyOn(router, 'navigate'); + spyOn((comp as any), 'refreshCache'); comp.onConfirm(data1); fixture.detectChanges(); expect(notificationsService.success).toHaveBeenCalled(); + expect((comp as any).refreshCache).toHaveBeenCalled(); expect(router.navigate).toHaveBeenCalled(); }); @@ -127,6 +158,78 @@ describe('DeleteComColPageComponent', () => { fixture.detectChanges(); expect(dsoDataService.delete).toHaveBeenCalledWith(data1.id); }); + + describe('cache refresh', () => { + let scheduler; + let communityWithoutParentHref; + let deletedCommunity; + + beforeEach(() => { + scheduler = getTestScheduler(); + + }) + describe('cache refreshed top level community', () => { + beforeEach(() => { + (dsoDataService.findByHref as any).and.returnValue(createNoContentRemoteDataObject$()); + deletedCommunity = { + dso: Object.assign(new Community(), { + metadata: [{ + key: 'dc.title', + value: 'top level community' + }] + }), + _links: { + parentCommunity: { + href: 'topLevel/parentCommunity' + } + } + }; + communityWithoutParentHref = { + dso: Object.assign(new Community(), { + metadata: [{ + key: 'dc.title', + value: 'top level community' + }] + }), + _links: {} + }; + }); + it('top level community cache refreshed', () => { + scheduler.schedule(() => (comp as any).refreshCache(deletedCommunity)); + scheduler.flush(); + expect(requestServiceStub.removeByHrefSubstring).toHaveBeenCalledWith('communities/search/top'); + }); + it('top level community without parent link, cache not refreshed', () => { + scheduler.schedule(() => (comp as any).refreshCache(communityWithoutParentHref)); + scheduler.flush(); + expect(requestServiceStub.removeByHrefSubstring).not.toHaveBeenCalled(); + }); + }); + + describe('cache refreshed child community', () => { + beforeEach(() => { + (dsoDataService.findByHref as any).and.returnValue(createSuccessfulRemoteDataObject$(parentCommunity)); + deletedCommunity = { + dso: Object.assign(new Community(), { + metadata: [{ + key: 'dc.title', + value: 'child community' + }] + }), + _links: { + parentCommunity: { + href: 'child/parentCommunity' + } + } + }; + }); + it('child level community cache refreshed', () => { + scheduler.schedule(() => (comp as any).refreshCache(deletedCommunity)); + scheduler.flush(); + expect(requestServiceStub.removeByHrefSubstring).toHaveBeenCalledWith('a20da287-e174-466a-9926-f66as300d399'); + }); + }); + }); }); describe('onCancel', () => { diff --git a/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts b/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts index 29ec4f7b19..835d302de3 100644 --- a/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts +++ b/src/app/shared/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts @@ -10,7 +10,9 @@ import {TranslateService} from '@ngx-translate/core'; import {RestResponse} from '../../../core/cache/response.models'; import {hasValue, isEmpty, isNotEmpty} from '../../empty.util'; import {RequestService} from '../../../core/data/request.service'; -import {getRemoteDataPayload, getSucceededRemoteData} from '../../../core/shared/operators'; +import { + getSucceededOrNoContentResponse, +} from '../../../core/shared/operators'; import {Community} from '../../../core/shared/community.model'; import {Collection} from '../../../core/shared/collection.model'; @@ -74,16 +76,17 @@ export class DeleteComColPageComponent implements } private refreshCache(dso: TDomain) { - const parentCommunity = this.parentCommunityUrl(dso as any); - if (!hasValue(parentCommunity)) { + const parentCommunityUrl = this.parentCommunityUrl(dso as any); + if (!hasValue(parentCommunityUrl)) { return; } - this.dsoDataService.findByHref(parentCommunity).pipe( - getSucceededRemoteData(), - getRemoteDataPayload(), - map((pc: TDomain) => isEmpty(pc) ? 'communities/search/top' : pc.id ), - take(1) - ).subscribe((id: string) => this.requestService.removeByHrefSubstring(id)); + this.dsoDataService.findByHref(parentCommunityUrl).pipe( + getSucceededOrNoContentResponse(), + take(1), + ).subscribe((rd: RemoteData) => { + const href = rd.hasSucceeded && !isEmpty(rd.payload.id) ? rd.payload.id : 'communities/search/top'; + this.requestService.removeByHrefSubstring(href) + }); } private parentCommunityUrl(dso: Collection | Community): string {