diff --git a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.spec.ts b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.spec.ts index a507e8e585..a1951a6d5a 100644 --- a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.spec.ts +++ b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.spec.ts @@ -11,7 +11,6 @@ import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.serv import { MockRouter } from '../../shared/mocks/mock-router'; import { ChangeDetectorRef, NO_ERRORS_SCHEMA } from '@angular/core'; import { of as observableOf } from 'rxjs/internal/observable/of'; -import { RemoteData } from '../../core/data/remote-data'; import { ActivatedRouteStub } from '../../shared/testing/active-router-stub'; import { Community } from '../../core/shared/community.model'; import { Item } from '../../core/shared/item.model'; diff --git a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.ts b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.ts index 98d0cc9cd1..34f9ff200e 100644 --- a/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.ts +++ b/src/app/+browse-by/+browse-by-date-page/browse-by-date-page.component.ts @@ -82,7 +82,8 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { const date = firstItemRD.payload.firstMetadataValue(metadataField); if (hasValue(date)) { const dateObj = new Date(date); - lowerLimit = dateObj.getFullYear(); + // TODO: it appears that getFullYear (based on local time) is sometimes unreliable. Switching to UTC. + lowerLimit = dateObj.getUTCFullYear(); } } const options = []; diff --git a/src/app/+my-dspace-page/my-dspace-page.component.spec.ts b/src/app/+my-dspace-page/my-dspace-page.component.spec.ts index 27daa30a0f..653f4a4844 100644 --- a/src/app/+my-dspace-page/my-dspace-page.component.spec.ts +++ b/src/app/+my-dspace-page/my-dspace-page.component.spec.ts @@ -15,7 +15,6 @@ import { SortDirection, SortOptions } from '../core/cache/models/sort-options.mo import { CommunityDataService } from '../core/data/community-data.service'; import { HostWindowService } from '../shared/host-window.service'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; -import { RemoteData } from '../core/data/remote-data'; import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from './my-dspace-page.component'; import { RouteService } from '../core/services/route.service'; import { routeServiceStub } from '../shared/testing/route-service-stub'; @@ -50,6 +49,7 @@ describe('MyDSpacePageComponent', () => { const mockResults = createSuccessfulRemoteDataObject$(['test', 'data']); const searchServiceStub = jasmine.createSpyObj('SearchService', { search: mockResults, + getEndpoint: observableOf('discover/search/objects'), getSearchLink: '/mydspace', getScopes: observableOf(['test-scope']), setServiceOptions: {} @@ -76,6 +76,7 @@ describe('MyDSpacePageComponent', () => { scope: scopeParam }) }; + const sidebarService = { isCollapsed: observableOf(true), collapse: () => this.isCollapsed = observableOf(true), diff --git a/src/app/+my-dspace-page/my-dspace-page.component.ts b/src/app/+my-dspace-page/my-dspace-page.component.ts index 3600a10d22..ced237f682 100644 --- a/src/app/+my-dspace-page/my-dspace-page.component.ts +++ b/src/app/+my-dspace-page/my-dspace-page.component.ts @@ -124,13 +124,13 @@ export class MyDSpacePageComponent implements OnInit { ngOnInit(): void { this.configurationList$ = this.searchConfigService.getAvailableConfigurationOptions(); this.searchOptions$ = this.searchConfigService.paginatedSearchOptions; - this.sub = this.searchOptions$.pipe( tap(() => this.resultsRD$.next(null)), switchMap((options: PaginatedSearchOptions) => this.service.search(options).pipe(getSucceededRemoteData()))) .subscribe((results) => { this.resultsRD$.next(results); }); + this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe( switchMap((scopeId) => this.service.getScopes(scopeId)) ); diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 22a1aba637..d013d9cc64 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -97,14 +97,8 @@ export class SearchService implements OnDestroy { } } - /** - * Method to retrieve a paginated list of search results from the server - * @param {PaginatedSearchOptions} searchOptions The configuration necessary to perform this search - * @param responseMsToLive The amount of milliseconds for the response to live in cache - * @returns {Observable>>>} Emits a paginated list with all search results found - */ - search(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number): Observable>>> { - const hrefObs = this.halService.getEndpoint(this.searchLinkPath).pipe( + getEndpoint(searchOptions?: PaginatedSearchOptions): Observable { + return this.halService.getEndpoint(this.searchLinkPath).pipe( map((url: string) => { if (hasValue(searchOptions)) { return (searchOptions as PaginatedSearchOptions).toRestUrl(url); @@ -113,6 +107,17 @@ export class SearchService implements OnDestroy { } }) ); + } + + /** + * Method to retrieve a paginated list of search results from the server + * @param {PaginatedSearchOptions} searchOptions The configuration necessary to perform this search + * @param responseMsToLive The amount of milliseconds for the response to live in cache + * @returns {Observable>>>} Emits a paginated list with all search results found + */ + search(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number): Observable>>> { + + const hrefObs = this.getEndpoint(searchOptions); const requestObs = hrefObs.pipe( map((url: string) => { diff --git a/src/app/core/auth/auth-request.service.ts b/src/app/core/auth/auth-request.service.ts index cbabe5c3fd..8773b1a9fb 100644 --- a/src/app/core/auth/auth-request.service.ts +++ b/src/app/core/auth/auth-request.service.ts @@ -44,7 +44,11 @@ export class AuthRequestService { map((endpointURL) => this.getEndpointByMethod(endpointURL, method)), distinctUntilChanged(), map((endpointURL: string) => new AuthPostRequest(this.requestService.generateRequestId(), endpointURL, body, options)), - tap((request: PostRequest) => this.requestService.configure(request, true)), + map ((request: PostRequest) => { + request.responseMsToLive = 10 * 1000; + return request; + }), + tap((request: PostRequest) => this.requestService.configure(request)), mergeMap((request: PostRequest) => this.fetchRequest(request)), distinctUntilChanged()); } @@ -55,7 +59,11 @@ export class AuthRequestService { map((endpointURL) => this.getEndpointByMethod(endpointURL, method)), distinctUntilChanged(), map((endpointURL: string) => new AuthGetRequest(this.requestService.generateRequestId(), endpointURL, options)), - tap((request: GetRequest) => this.requestService.configure(request, true)), + map ((request: GetRequest) => { + request.responseMsToLive = 10 * 1000; + return request; + }), + tap((request: GetRequest) => this.requestService.configure(request)), mergeMap((request: GetRequest) => this.fetchRequest(request)), distinctUntilChanged()); } diff --git a/src/app/core/auth/auth-response-parsing.service.ts b/src/app/core/auth/auth-response-parsing.service.ts index a5a160531c..8137734c49 100644 --- a/src/app/core/auth/auth-response-parsing.service.ts +++ b/src/app/core/auth/auth-response-parsing.service.ts @@ -25,7 +25,7 @@ export class AuthResponseParsingService extends BaseResponseParsingService imple parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && (data.statusCode === 200)) { - const response = this.process>(data.payload, request.uuid); + const response = this.process>(data.payload, request); return new AuthStatusResponse(response, data.statusCode, data.statusText); } else { return new AuthStatusResponse(data.payload as NormalizedAuthStatus, data.statusCode, data.statusText); diff --git a/src/app/core/browse/browse.service.spec.ts b/src/app/core/browse/browse.service.spec.ts index 725b371c14..55ff7a090e 100644 --- a/src/app/core/browse/browse.service.spec.ts +++ b/src/app/core/browse/browse.service.spec.ts @@ -114,7 +114,7 @@ describe('BrowseService', () => { scheduler.schedule(() => service.getBrowseDefinitions().subscribe()); scheduler.flush(); - expect(requestService.configure).toHaveBeenCalledWith(expected, undefined); + expect(requestService.configure).toHaveBeenCalledWith(expected); }); it('should call RemoteDataBuildService to create the RemoteData Observable', () => { @@ -155,7 +155,7 @@ describe('BrowseService', () => { scheduler.schedule(() => service.getBrowseEntriesFor(new BrowseEntrySearchOptions(browseDefinitions[1].id)).subscribe()); scheduler.flush(); - expect(requestService.configure).toHaveBeenCalledWith(expected, undefined); + expect(requestService.configure).toHaveBeenCalledWith(expected); }); it('should call RemoteDataBuildService to create the RemoteData Observable', () => { @@ -174,7 +174,7 @@ describe('BrowseService', () => { scheduler.schedule(() => service.getBrowseItemsFor(mockAuthorName, new BrowseEntrySearchOptions(browseDefinitions[1].id)).subscribe()); scheduler.flush(); - expect(requestService.configure).toHaveBeenCalledWith(expected, undefined); + expect(requestService.configure).toHaveBeenCalledWith(expected); }); it('should call RemoteDataBuildService to create the RemoteData Observable', () => { @@ -303,7 +303,7 @@ describe('BrowseService', () => { scheduler.schedule(() => service.getFirstItemFor(browseDefinitions[1].id).subscribe()); scheduler.flush(); - expect(requestService.configure).toHaveBeenCalledWith(expected, undefined); + expect(requestService.configure).toHaveBeenCalledWith(expected); }); it('should call RemoteDataBuildService to create the RemoteData Observable', () => { diff --git a/src/app/core/config/config-response-parsing.service.ts b/src/app/core/config/config-response-parsing.service.ts index 08fe581406..d1f49710d3 100644 --- a/src/app/core/config/config-response-parsing.service.ts +++ b/src/app/core/config/config-response-parsing.service.ts @@ -24,7 +24,7 @@ export class ConfigResponseParsingService extends BaseResponseParsingService imp parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && (data.statusCode === 201 || data.statusCode === 200)) { - const configDefinition = this.process(data.payload, request.uuid); + const configDefinition = this.process(data.payload, request); return new ConfigSuccessResponse(configDefinition, data.statusCode, data.statusText, this.processPageInfo(data.payload)); } else { return new ErrorResponse( diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index 0ed5dc363c..ea2d71faa7 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -9,6 +9,7 @@ import { PaginatedList } from './paginated-list'; import { isRestDataObject, isRestPaginatedList } from '../cache/builders/normalized-object-build.service'; import { ResourceType } from '../shared/resource-type'; import { getMapsToType } from '../cache/builders/build-decorators'; +import { RestRequest } from './request.models'; /* tslint:disable:max-classes-per-file */ export abstract class BaseResponseParsingService { @@ -16,14 +17,14 @@ export abstract class BaseResponseParsingService { protected abstract objectCache: ObjectCacheService; protected abstract toCache: boolean; - protected process(data: any, requestUUID: string): any { + protected process(data: any, request: RestRequest): any { if (isNotEmpty(data)) { if (hasNoValue(data) || (typeof data !== 'object')) { return data; } else if (isRestPaginatedList(data)) { - return this.processPaginatedList(data, requestUUID); + return this.processPaginatedList(data, request); } else if (Array.isArray(data)) { - return this.processArray(data, requestUUID); + return this.processArray(data, request); } else if (isRestDataObject(data)) { const object = this.deserialize(data); if (isNotEmpty(data._embedded)) { @@ -31,7 +32,7 @@ export abstract class BaseResponseParsingService { .keys(data._embedded) .filter((property) => data._embedded.hasOwnProperty(property)) .forEach((property) => { - const parsedObj = this.process(data._embedded[property], requestUUID); + const parsedObj = this.process(data._embedded[property], request); if (isNotEmpty(parsedObj)) { if (isRestPaginatedList(data._embedded[property])) { object[property] = parsedObj; @@ -45,7 +46,7 @@ export abstract class BaseResponseParsingService { }); } - this.cache(object, requestUUID); + this.cache(object, request); return object; } const result = {}; @@ -53,14 +54,14 @@ export abstract class BaseResponseParsingService { .filter((property) => data.hasOwnProperty(property)) .filter((property) => hasValue(data[property])) .forEach((property) => { - result[property] = this.process(data[property], requestUUID); + result[property] = this.process(data[property], request); }); return result; } } - protected processPaginatedList(data: any, requestUUID: string): PaginatedList { + protected processPaginatedList(data: any, request: RestRequest): PaginatedList { const pageInfo: PageInfo = this.processPageInfo(data); let list = data._embedded; @@ -70,14 +71,14 @@ export abstract class BaseResponseParsingService { } else if (!Array.isArray(list)) { list = this.flattenSingleKeyObject(list); } - const page: ObjectDomain[] = this.processArray(list, requestUUID); + const page: ObjectDomain[] = this.processArray(list, request); return new PaginatedList(pageInfo, page, ); } - protected processArray(data: any, requestUUID: string): ObjectDomain[] { + protected processArray(data: any, request: RestRequest): ObjectDomain[] { let array: ObjectDomain[] = []; data.forEach((datum) => { - array = [...array, this.process(datum, requestUUID)]; + array = [...array, this.process(datum, request)]; } ); return array; @@ -104,17 +105,17 @@ export abstract class BaseResponseParsingService { } } - protected cache(obj, requestUUID) { + protected cache(obj, request: RestRequest) { if (this.toCache) { - this.addToObjectCache(obj, requestUUID); + this.addToObjectCache(obj, request); } } - protected addToObjectCache(co: CacheableObject, requestUUID: string): void { + protected addToObjectCache(co: CacheableObject, request: RestRequest): void { if (hasNoValue(co) || hasNoValue(co.self)) { throw new Error('The server returned an invalid object'); } - this.objectCache.add(co, this.EnvConfig.cache.msToLive.default, requestUUID); + this.objectCache.add(co, hasValue(request.responseMsToLive) ? request.responseMsToLive : this.EnvConfig.cache.msToLive.default, request.uuid); } processPageInfo(payload: any): PageInfo { diff --git a/src/app/core/data/bitstream-format-data.service.ts b/src/app/core/data/bitstream-format-data.service.ts index a5638183c0..bdf9b16acf 100644 --- a/src/app/core/data/bitstream-format-data.service.ts +++ b/src/app/core/data/bitstream-format-data.service.ts @@ -38,7 +38,6 @@ const selectedBitstreamFormatSelector = createSelector(bitstreamFormatsStateSele export class BitstreamFormatDataService extends DataService { protected linkPath = 'bitstreamformats'; - protected forceBypassCache = false; constructor( protected requestService: RequestService, diff --git a/src/app/core/data/collection-data.service.spec.ts b/src/app/core/data/collection-data.service.spec.ts index 5cb7fed5e4..a3e1a916e3 100644 --- a/src/app/core/data/collection-data.service.spec.ts +++ b/src/app/core/data/collection-data.service.spec.ts @@ -37,7 +37,7 @@ describe('CollectionDataService', () => { }); it('should configure a GET request', () => { - expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(GetRequest), undefined); + expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(GetRequest)); }); }); diff --git a/src/app/core/data/collection-data.service.ts b/src/app/core/data/collection-data.service.ts index e49267d1f2..7ec31d8970 100644 --- a/src/app/core/data/collection-data.service.ts +++ b/src/app/core/data/collection-data.service.ts @@ -31,7 +31,6 @@ import { SearchParam } from '../cache/models/search-param.model'; @Injectable() export class CollectionDataService extends ComColDataService { protected linkPath = 'collections'; - protected forceBypassCache = false; constructor( protected requestService: RequestService, diff --git a/src/app/core/data/comcol-data.service.spec.ts b/src/app/core/data/comcol-data.service.spec.ts index b5232b0bff..5cc474dff9 100644 --- a/src/app/core/data/comcol-data.service.spec.ts +++ b/src/app/core/data/comcol-data.service.spec.ts @@ -28,7 +28,6 @@ class NormalizedTestObject extends NormalizedObject { } class TestService extends ComColDataService { - protected forceBypassCache = false; constructor( protected requestService: RequestService, diff --git a/src/app/core/data/community-data.service.ts b/src/app/core/data/community-data.service.ts index 8db4d762eb..cc55fe6869 100644 --- a/src/app/core/data/community-data.service.ts +++ b/src/app/core/data/community-data.service.ts @@ -24,7 +24,6 @@ export class CommunityDataService extends ComColDataService { protected linkPath = 'communities'; protected topLinkPath = 'communities/search/top'; protected cds = this; - protected forceBypassCache = false; constructor( protected requestService: RequestService, diff --git a/src/app/core/data/data.service.spec.ts b/src/app/core/data/data.service.spec.ts index dede6f8ae2..ab3cf0ac16 100644 --- a/src/app/core/data/data.service.spec.ts +++ b/src/app/core/data/data.service.spec.ts @@ -24,7 +24,6 @@ class NormalizedTestObject extends NormalizedObject { } class TestService extends DataService { - protected forceBypassCache = false; constructor( protected requestService: RequestService, diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index ad0db51980..34215f1a2f 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -45,11 +45,14 @@ export abstract class DataService { protected abstract store: Store; protected abstract linkPath: string; protected abstract halService: HALEndpointService; - protected abstract forceBypassCache = false; protected abstract objectCache: ObjectCacheService; protected abstract notificationsService: NotificationsService; protected abstract http: HttpClient; protected abstract comparator: ChangeAnalyzer; + /** + * Allows subclasses to reset the response cache time. + */ + protected responseMsToLive: number; public abstract getBrowseEndpoint(options: FindAllOptions, linkPath?: string): Observable @@ -131,7 +134,10 @@ export abstract class DataService { first((href: string) => hasValue(href))) .subscribe((href: string) => { const request = new FindAllRequest(this.requestService.generateRequestId(), href, options); - this.requestService.configure(request, this.forceBypassCache); + if (hasValue(this.responseMsToLive)) { + request.responseMsToLive = this.responseMsToLive; + } + this.requestService.configure(request); }); return this.rdbService.buildList(hrefObs) as Observable>>; @@ -154,14 +160,21 @@ export abstract class DataService { find((href: string) => hasValue(href))) .subscribe((href: string) => { const request = new FindByIDRequest(this.requestService.generateRequestId(), href, id); - this.requestService.configure(request, this.forceBypassCache); + if (hasValue(this.responseMsToLive)) { + request.responseMsToLive = this.responseMsToLive; + } + this.requestService.configure(request); }); return this.rdbService.buildSingle(hrefObs); } findByHref(href: string, options?: HttpOptions): Observable> { - this.requestService.configure(new GetRequest(this.requestService.generateRequestId(), href, null, options), this.forceBypassCache); + const request = new GetRequest(this.requestService.generateRequestId(), href, null, options); + if (hasValue(this.responseMsToLive)) { + request.responseMsToLive = this.responseMsToLive; + } + this.requestService.configure(request); return this.rdbService.buildSingle(href); } @@ -192,7 +205,8 @@ export abstract class DataService { first((href: string) => hasValue(href))) .subscribe((href: string) => { const request = new FindAllRequest(this.requestService.generateRequestId(), href, options); - this.requestService.configure(request, true); + request.responseMsToLive = 10 * 1000; + this.requestService.configure(request); }); return this.rdbService.buildList(hrefObs) as Observable>>; diff --git a/src/app/core/data/dso-response-parsing.service.ts b/src/app/core/data/dso-response-parsing.service.ts index d6c3b2caa6..d2c21825cc 100644 --- a/src/app/core/data/dso-response-parsing.service.ts +++ b/src/app/core/data/dso-response-parsing.service.ts @@ -30,7 +30,7 @@ export class DSOResponseParsingService extends BaseResponseParsingService implem if (hasValue(data.payload) && hasValue(data.payload.page) && data.payload.page.totalElements === 0) { processRequestDTO = { page: [] }; } else { - processRequestDTO = this.process>(data.payload, request.uuid); + processRequestDTO = this.process>(data.payload, request); } let objectList = processRequestDTO; diff --git a/src/app/core/data/dspace-object-data.service.spec.ts b/src/app/core/data/dspace-object-data.service.spec.ts index a0bba214ae..7047db6065 100644 --- a/src/app/core/data/dspace-object-data.service.spec.ts +++ b/src/app/core/data/dspace-object-data.service.spec.ts @@ -72,7 +72,7 @@ describe('DSpaceObjectDataService', () => { scheduler.schedule(() => service.findById(testObject.uuid)); scheduler.flush(); - expect(requestService.configure).toHaveBeenCalledWith(new FindByIDRequest(requestUUID, requestURL, testObject.uuid), false); + expect(requestService.configure).toHaveBeenCalledWith(new FindByIDRequest(requestUUID, requestURL, testObject.uuid)); }); it('should return a RemoteData for the object with the given ID', () => { diff --git a/src/app/core/data/dspace-object-data.service.ts b/src/app/core/data/dspace-object-data.service.ts index 4f0653f416..bb02afbcd1 100644 --- a/src/app/core/data/dspace-object-data.service.ts +++ b/src/app/core/data/dspace-object-data.service.ts @@ -18,7 +18,6 @@ import { DSOChangeAnalyzer } from './dso-change-analyzer.service'; /* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { protected linkPath = 'dso'; - protected forceBypassCache = false; constructor( protected requestService: RequestService, diff --git a/src/app/core/data/item-data.service.ts b/src/app/core/data/item-data.service.ts index de05dad0c1..e616cb8020 100644 --- a/src/app/core/data/item-data.service.ts +++ b/src/app/core/data/item-data.service.ts @@ -41,7 +41,6 @@ import { PaginatedList } from './paginated-list'; @Injectable() export class ItemDataService extends DataService { protected linkPath = 'items'; - protected forceBypassCache = false; constructor( protected requestService: RequestService, diff --git a/src/app/core/data/metadata-schema-data.service.ts b/src/app/core/data/metadata-schema-data.service.ts index 4baca6e8ed..b15dd6865f 100644 --- a/src/app/core/data/metadata-schema-data.service.ts +++ b/src/app/core/data/metadata-schema-data.service.ts @@ -19,7 +19,6 @@ import { MetadataSchema } from '../metadata/metadata-schema.model'; /* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { protected linkPath = 'metadataschemas'; - protected forceBypassCache = false; constructor( protected requestService: RequestService, diff --git a/src/app/core/data/relationship.service.spec.ts b/src/app/core/data/relationship.service.spec.ts index 1563d6ad63..4091759386 100644 --- a/src/app/core/data/relationship.service.spec.ts +++ b/src/app/core/data/relationship.service.spec.ts @@ -114,7 +114,7 @@ describe('RelationshipService', () => { it('should send a DeleteRequest', () => { const expected = new DeleteRequest(requestService.generateRequestId(), relationshipsEndpointURL + '/' + relationship1.uuid); - expect(requestService.configure).toHaveBeenCalledWith(expected, undefined); + expect(requestService.configure).toHaveBeenCalledWith(expected); }); it('should clear the related items their cache', () => { diff --git a/src/app/core/data/request.models.ts b/src/app/core/data/request.models.ts index 43ab9e58e7..d7dfa16024 100644 --- a/src/app/core/data/request.models.ts +++ b/src/app/core/data/request.models.ts @@ -23,7 +23,8 @@ import { MappedCollectionsReponseParsingService } from './mapped-collections-rep /* tslint:disable:max-classes-per-file */ export abstract class RestRequest { - public responseMsToLive = 0; + public responseMsToLive = 10 * 1000; + public forceBypassCache = false; constructor( public uuid: string, public href: string, @@ -293,6 +294,7 @@ export class UpdateMetadataFieldRequest extends PutRequest { * Class representing a submission HTTP GET request object */ export class SubmissionRequest extends GetRequest { + forceBypassCache = true; constructor(uuid: string, href: string) { super(uuid, href); } @@ -404,7 +406,7 @@ export class TaskDeleteRequest extends DeleteRequest { } export class MyDSpaceRequest extends GetRequest { - public responseMsToLive = 0; + public responseMsToLive = 10 * 1000; } export class RequestError extends Error { diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts index 0980d48537..bd65163892 100644 --- a/src/app/core/data/request.service.ts +++ b/src/app/core/data/request.service.ts @@ -19,7 +19,7 @@ import { } from '../index/index.selectors'; import { UUIDService } from '../shared/uuid.service'; import { RequestConfigureAction, RequestExecuteAction, RequestRemoveAction } from './request.actions'; -import { GetRequest, RestRequest } from './request.models'; +import { GetRequest, RestRequest, SubmissionRequest } from './request.models'; import { RequestEntry, RequestState } from './request.reducer'; import { CommitSSBAction } from '../cache/server-sync-buffer.actions'; import { RestRequestMethod } from './rest-request-method'; @@ -145,14 +145,10 @@ export class RequestService { * Configure a certain request * Used to make sure a request is in the cache * @param {RestRequest} request The request to send out - * @param {boolean} forceBypassCache When true, a new request is always dispatched */ - configure(request: RestRequest, forceBypassCache: boolean = false): void { + configure(request: RestRequest): void { const isGetRequest = request.method === RestRequestMethod.GET; - if (forceBypassCache) { - this.clearRequestsOnTheirWayToTheStore(request); - } - if (!isGetRequest || (forceBypassCache && !this.isPending(request)) || !this.isCachedOrPending(request)) { + if (!isGetRequest || request.forceBypassCache || !this.isCachedOrPending(request)) { this.dispatchRequest(request); if (isGetRequest) { this.trackRequestsOnTheirWayToTheStore(request); @@ -226,7 +222,6 @@ export class RequestService { const inReqCache = this.hasByHref(request.href); const inObjCache = this.objectCache.hasBySelfLink(request.href); const isCached = inReqCache || inObjCache; - const isPending = this.isPending(request); return isCached || isPending; } diff --git a/src/app/core/data/resource-policy.service.spec.ts b/src/app/core/data/resource-policy.service.spec.ts index 35d28684a7..1a02171be3 100644 --- a/src/app/core/data/resource-policy.service.spec.ts +++ b/src/app/core/data/resource-policy.service.spec.ts @@ -61,7 +61,7 @@ describe('ResourcePolicyService', () => { scheduler.schedule(() => service.findByHref(requestURL)); scheduler.flush(); - expect(requestService.configure).toHaveBeenCalledWith(new GetRequest(requestUUID, requestURL, null), false); + expect(requestService.configure).toHaveBeenCalledWith(new GetRequest(requestUUID, requestURL, null)); }); it('should return a RemoteData for the object with the given URL', () => { diff --git a/src/app/core/data/resource-policy.service.ts b/src/app/core/data/resource-policy.service.ts index 1a6a1afedc..1574111232 100644 --- a/src/app/core/data/resource-policy.service.ts +++ b/src/app/core/data/resource-policy.service.ts @@ -22,7 +22,6 @@ import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; /* tslint:disable:max-classes-per-file */ class DataServiceImpl extends DataService { protected linkPath = 'resourcepolicies'; - protected forceBypassCache = false; constructor( protected requestService: RequestService, diff --git a/src/app/core/eperson/eperson-response-parsing.service.ts b/src/app/core/eperson/eperson-response-parsing.service.ts index 481f37d1fa..76222323ae 100644 --- a/src/app/core/eperson/eperson-response-parsing.service.ts +++ b/src/app/core/eperson/eperson-response-parsing.service.ts @@ -28,7 +28,7 @@ export class EpersonResponseParsingService extends BaseResponseParsingService im parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links)) { - const epersonDefinition = this.process(data.payload, request.href); + const epersonDefinition = this.process(data.payload, request); return new EpersonSuccessResponse(epersonDefinition[Object.keys(epersonDefinition)[0]], data.statusCode, data.statusText, this.processPageInfo(data.payload)); } else { return new ErrorResponse( diff --git a/src/app/core/eperson/group-eperson.service.ts b/src/app/core/eperson/group-eperson.service.ts index 07a1bb6aba..2014e6120a 100644 --- a/src/app/core/eperson/group-eperson.service.ts +++ b/src/app/core/eperson/group-eperson.service.ts @@ -27,7 +27,6 @@ import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; export class GroupEpersonService extends EpersonService { protected linkPath = 'groups'; protected browseEndpoint = ''; - protected forceBypassCache = false; constructor( protected comparator: DSOChangeAnalyzer, diff --git a/src/app/core/integration/integration-response-parsing.service.ts b/src/app/core/integration/integration-response-parsing.service.ts index 8cc0f8d252..0609cd804e 100644 --- a/src/app/core/integration/integration-response-parsing.service.ts +++ b/src/app/core/integration/integration-response-parsing.service.ts @@ -27,7 +27,7 @@ export class IntegrationResponseParsingService extends BaseResponseParsingServic parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links)) { - const dataDefinition = this.process(data.payload, request.uuid); + const dataDefinition = this.process(data.payload, request); return new IntegrationSuccessResponse(this.processResponse(dataDefinition), data.statusCode, data.statusText, this.processPageInfo(data.payload)); } else { return new ErrorResponse( diff --git a/src/app/core/shared/operators.spec.ts b/src/app/core/shared/operators.spec.ts index 548a3f1339..ec069772f8 100644 --- a/src/app/core/shared/operators.spec.ts +++ b/src/app/core/shared/operators.spec.ts @@ -148,7 +148,7 @@ describe('Core Module - RxJS Operators', () => { scheduler.schedule(() => source.pipe(configureRequest(requestService)).subscribe()); scheduler.flush(); - expect(requestService.configure).toHaveBeenCalledWith(testRequest, undefined); + expect(requestService.configure).toHaveBeenCalledWith(testRequest); }); }); diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index d46c688e68..b4c7c8f1a9 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -52,9 +52,9 @@ export const getResourceLinksFromResponse = () => map((response: DSOSuccessResponse) => response.resourceSelfLinks), ); -export const configureRequest = (requestService: RequestService, forceBypassCache?: boolean) => +export const configureRequest = (requestService: RequestService) => (source: Observable): Observable => - source.pipe(tap((request: RestRequest) => requestService.configure(request, forceBypassCache))); + source.pipe(tap((request: RestRequest) => requestService.configure(request))); export const getRemoteDataPayload = () => (source: Observable>): Observable => diff --git a/src/app/core/submission/submission-response-parsing.service.ts b/src/app/core/submission/submission-response-parsing.service.ts index de7d683d91..da3b578fcd 100644 --- a/src/app/core/submission/submission-response-parsing.service.ts +++ b/src/app/core/submission/submission-response-parsing.service.ts @@ -91,7 +91,7 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links) && this.isSuccessStatus(data.statusCode)) { - const dataDefinition = this.processResponse(data.payload, request.href); + const dataDefinition = this.processResponse(data.payload, request); return new SubmissionSuccessResponse(dataDefinition, data.statusCode, data.statusText, this.processPageInfo(data.payload)); } else if (isEmpty(data.payload) && this.isSuccessStatus(data.statusCode)) { return new SubmissionSuccessResponse(null, data.statusCode, data.statusText); @@ -109,11 +109,11 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService * Parses response and normalize it * * @param {DSpaceRESTV2Response} data - * @param {string} requestHref + * @param {RestRequest} request * @returns {any[]} */ - protected processResponse(data: any, requestHref: string): any[] { - const dataDefinition = this.process(data, requestHref); + protected processResponse(data: any, request: RestRequest): any[] { + const dataDefinition = this.process(data, request); const normalizedDefinition = Array.of(); const processedList = Array.isArray(dataDefinition) ? dataDefinition : Array.of(dataDefinition); diff --git a/src/app/core/submission/submission-rest.service.spec.ts b/src/app/core/submission/submission-rest.service.spec.ts index 6e748c5575..30fe9f9163 100644 --- a/src/app/core/submission/submission-rest.service.spec.ts +++ b/src/app/core/submission/submission-rest.service.spec.ts @@ -59,10 +59,13 @@ describe('SubmissionRestService test suite', () => { describe('getDataById', () => { it('should configure a new SubmissionRequest', () => { const expected = new SubmissionRequest(requestService.generateRequestId(), resourceHref); + // set cache time to zero + expected.responseMsToLive = 0; + expected.forceBypassCache = true; scheduler.schedule(() => service.getDataById(resourceEndpoint, resourceScope).subscribe()); scheduler.flush(); - expect(requestService.configure).toHaveBeenCalledWith(expected, true); + expect(requestService.configure).toHaveBeenCalledWith(expected); }); }); diff --git a/src/app/core/submission/submission-rest.service.ts b/src/app/core/submission/submission-rest.service.ts index e2b8bb01c8..58aa507314 100644 --- a/src/app/core/submission/submission-rest.service.ts +++ b/src/app/core/submission/submission-rest.service.ts @@ -6,7 +6,7 @@ import { distinctUntilChanged, filter, flatMap, map, mergeMap, tap } from 'rxjs/ import { RequestService } from '../data/request.service'; import { isNotEmpty } from '../../shared/empty.util'; import { - DeleteRequest, + DeleteRequest, GetRequest, PostRequest, RestRequest, SubmissionDeleteRequest, @@ -109,7 +109,11 @@ export class SubmissionRestService { filter((href: string) => isNotEmpty(href)), distinctUntilChanged(), map((endpointURL: string) => new SubmissionRequest(requestId, endpointURL)), - tap((request: RestRequest) => this.requestService.configure(request, true)), + map ((request: RestRequest) => { + request.responseMsToLive = 0; + return request; + }), + tap((request: RestRequest) => this.requestService.configure(request)), flatMap(() => this.fetchRequest(requestId)), distinctUntilChanged()); } diff --git a/src/app/core/submission/workflowitem-data.service.ts b/src/app/core/submission/workflowitem-data.service.ts index fd769745da..43c4aecafe 100644 --- a/src/app/core/submission/workflowitem-data.service.ts +++ b/src/app/core/submission/workflowitem-data.service.ts @@ -20,7 +20,7 @@ import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; @Injectable() export class WorkflowItemDataService extends DataService { protected linkPath = 'workflowitems'; - protected forceBypassCache = true; + protected responseMsToLive = 10 * 1000; constructor( protected comparator: DSOChangeAnalyzer, diff --git a/src/app/core/submission/workspaceitem-data.service.ts b/src/app/core/submission/workspaceitem-data.service.ts index de671d57c7..4d388ec513 100644 --- a/src/app/core/submission/workspaceitem-data.service.ts +++ b/src/app/core/submission/workspaceitem-data.service.ts @@ -20,7 +20,7 @@ import { WorkspaceItem } from './models/workspaceitem.model'; @Injectable() export class WorkspaceitemDataService extends DataService { protected linkPath = 'workspaceitems'; - protected forceBypassCache = true; + protected responseMsToLive = 10 * 1000; constructor( protected comparator: DSOChangeAnalyzer, diff --git a/src/app/core/tasks/claimed-task-data.service.ts b/src/app/core/tasks/claimed-task-data.service.ts index dda42e1c67..76e5e769d7 100644 --- a/src/app/core/tasks/claimed-task-data.service.ts +++ b/src/app/core/tasks/claimed-task-data.service.ts @@ -22,16 +22,13 @@ import { ProcessTaskResponse } from './models/process-task-response'; @Injectable() export class ClaimedTaskDataService extends TasksService { + protected responseMsToLive = 10 * 1000; + /** * The endpoint link name */ protected linkPath = 'claimedtasks'; - /** - * When true, a new request is always dispatched - */ - protected forceBypassCache = true; - /** * Initialize instance variables * diff --git a/src/app/core/tasks/pool-task-data.service.ts b/src/app/core/tasks/pool-task-data.service.ts index 1a93450d4d..0e7704336d 100644 --- a/src/app/core/tasks/pool-task-data.service.ts +++ b/src/app/core/tasks/pool-task-data.service.ts @@ -27,10 +27,7 @@ export class PoolTaskDataService extends TasksService { */ protected linkPath = 'pooltasks'; - /** - * When true, a new request is always dispatched - */ - protected forceBypassCache = true; + protected responseMsToLive = 10 * 1000; /** * Initialize instance variables diff --git a/src/app/core/tasks/tasks.service.spec.ts b/src/app/core/tasks/tasks.service.spec.ts index 753ce2ddd5..3ca9b8ea8f 100644 --- a/src/app/core/tasks/tasks.service.spec.ts +++ b/src/app/core/tasks/tasks.service.spec.ts @@ -29,7 +29,6 @@ class TestTask extends TaskObject { class TestService extends TasksService { protected linkPath = LINK_NAME; - protected forceBypassCache = true; constructor( protected requestService: RequestService, diff --git a/src/app/shared/mocks/mock-search-service.ts b/src/app/shared/mocks/mock-search-service.ts new file mode 100644 index 0000000000..20da2049d1 --- /dev/null +++ b/src/app/shared/mocks/mock-search-service.ts @@ -0,0 +1,12 @@ +import { of as observableOf } from 'rxjs'; +import { SearchService } from '../../+search-page/search-service/search.service'; + +export function getMockSearchService(): SearchService { + return jasmine.createSpyObj('searchService', { + search: '', + getEndpoint: observableOf('discover/search/objects'), + getSearchLink: '/mydspace', + getScopes: observableOf(['test-scope']), + setServiceOptions: {} + }); +} diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.spec.ts index 5084b3e9fe..5f62d733f4 100644 --- a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.spec.ts +++ b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.spec.ts @@ -18,6 +18,10 @@ import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.mode import { WorkflowItem } from '../../../core/submission/models/workflowitem.model'; import { createSuccessfulRemoteDataObject } from '../../testing/utils'; import { CoreModule } from '../../../core/core.module'; +import { getMockSearchService } from '../../mocks/mock-search-service'; +import { getMockRequestService } from '../../mocks/mock-request.service'; +import { SearchService } from '../../../+search-page/search-service/search.service'; +import { RequestService } from '../../../core/data/request.service'; let component: ClaimedTaskActionsComponent; let fixture: ComponentFixture; @@ -32,6 +36,10 @@ const mockDataService = jasmine.createSpyObj('ClaimedTaskDataService', { returnToPoolTask: jasmine.createSpy('returnToPoolTask'), }); +const searchService = getMockSearchService(); + +const requestServce = getMockRequestService(); + const item = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: { @@ -83,6 +91,8 @@ describe('ClaimedTaskActionsComponent', () => { { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: Router, useValue: new RouterStub() }, { provide: ClaimedTaskDataService, useValue: mockDataService }, + { provide: SearchService, useValue: searchService }, + { provide: RequestService, useValue: requestServce } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(ClaimedTaskActionsComponent, { diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts index 554becdb27..6355b4eca1 100644 --- a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts @@ -13,6 +13,8 @@ import { WorkflowItem } from '../../../core/submission/models/workflowitem.model import { RemoteData } from '../../../core/data/remote-data'; import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { NotificationsService } from '../../notifications/notifications.service'; +import { SearchService } from '../../../+search-page/search-service/search.service'; +import { RequestService } from '../../../core/data/request.service'; /** * This component represents actions related to ClaimedTask object. @@ -56,12 +58,16 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent; @@ -50,6 +54,10 @@ mockObject = Object.assign(new Item(), { } }); +const searchService = getMockSearchService() + +const requestServce = getMockRequestService(); + describe('ItemActionsComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ @@ -67,6 +75,8 @@ describe('ItemActionsComponent', () => { { provide: Router, useValue: new RouterStub() }, { provide: ItemDataService, useValue: mockDataService }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: SearchService, useValue: searchService }, + { provide: RequestService, useValue: requestServce } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(ItemActionsComponent, { diff --git a/src/app/shared/mydspace-actions/item/item-actions.component.ts b/src/app/shared/mydspace-actions/item/item-actions.component.ts index eeb6e49ad9..7f6307c5ec 100644 --- a/src/app/shared/mydspace-actions/item/item-actions.component.ts +++ b/src/app/shared/mydspace-actions/item/item-actions.component.ts @@ -7,6 +7,8 @@ import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { ItemDataService } from '../../../core/data/item-data.service'; import { Item } from '../../../core/shared/item.model'; import { NotificationsService } from '../../notifications/notifications.service'; +import { SearchService } from '../../../+search-page/search-service/search.service'; +import { RequestService } from '../../../core/data/request.service'; /** * This component represents mydspace actions related to Item object. @@ -31,12 +33,16 @@ export class ItemActionsComponent extends MyDSpaceActionsComponent(); this.objectDataService = injector.get(factory.getConstructor(objectType)); } @@ -57,13 +66,18 @@ export abstract class MyDSpaceActionsComponent { return false; }; - this.router.navigated = false; - const url = decodeURIComponent(this.router.url); - this.router.navigateByUrl(url); + // This assures that the search cache is empty before reloading mydspace. + // See https://github.com/DSpace/dspace-angular/pull/468 + this.searchService.getEndpoint().pipe( + take(1), + tap((cachedHref) => this.requestService.removeByHrefSubstring(cachedHref)) + ).subscribe(() => this.router.navigateByUrl(url)); } /** diff --git a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts index c06cec3422..55dcb7f8c2 100644 --- a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts +++ b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts @@ -17,6 +17,10 @@ import { PoolTaskActionsComponent } from './pool-task-actions.component'; import { PoolTask } from '../../../core/tasks/models/pool-task-object.model'; import { WorkflowItem } from '../../../core/submission/models/workflowitem.model'; import { createSuccessfulRemoteDataObject } from '../../testing/utils'; +import { getMockRequestService } from '../../mocks/mock-request.service'; +import { SearchService } from '../../../+search-page/search-service/search.service'; +import { RequestService } from '../../../core/data/request.service'; +import { getMockSearchService } from '../../mocks/mock-search-service'; let component: PoolTaskActionsComponent; let fixture: ComponentFixture; @@ -29,6 +33,10 @@ const mockDataService = jasmine.createSpyObj('PoolTaskDataService', { claimTask: jasmine.createSpy('claimTask') }); +const searchService = getMockSearchService(); + +const requestServce = getMockRequestService(); + const item = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: { @@ -80,6 +88,8 @@ describe('PoolTaskActionsComponent', () => { { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: Router, useValue: new RouterStub() }, { provide: PoolTaskDataService, useValue: mockDataService }, + { provide: SearchService, useValue: searchService }, + { provide: RequestService, useValue: requestServce } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(PoolTaskActionsComponent, { @@ -167,4 +177,14 @@ describe('PoolTaskActionsComponent', () => { }); })); + it('should clear the object cache by href', async(() => { + component.reload(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(searchService.getEndpoint).toHaveBeenCalled(); + expect(requestServce.removeByHrefSubstring).toHaveBeenCalledWith('discover/search/objects'); + }); + })); + }); diff --git a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts index 2124c0d9b6..b284be6f0b 100644 --- a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts +++ b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts @@ -13,6 +13,8 @@ import { PoolTaskDataService } from '../../../core/tasks/pool-task-data.service' import { isNotUndefined } from '../../empty.util'; import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { NotificationsService } from '../../notifications/notifications.service'; +import { SearchService } from '../../../+search-page/search-service/search.service'; +import { RequestService } from '../../../core/data/request.service'; /** * This component represents mydspace actions related to PoolTask object. @@ -47,12 +49,16 @@ export class PoolTaskActionsComponent extends MyDSpaceActionsComponent; @@ -23,6 +27,10 @@ let mockObject: WorkflowItem; const mockDataService = {}; +const searchService = getMockSearchService(); + +const requestServce = getMockRequestService(); + const item = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: { @@ -72,6 +80,8 @@ describe('WorkflowitemActionsComponent', () => { { provide: Router, useValue: new RouterStub() }, { provide: WorkflowItemDataService, useValue: mockDataService }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: SearchService, useValue: searchService }, + { provide: RequestService, useValue: requestServce } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(WorkflowitemActionsComponent, { diff --git a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts index 453d875150..45e777cef8 100644 --- a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts +++ b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts @@ -7,6 +7,8 @@ import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { WorkflowItem } from '../../../core/submission/models/workflowitem.model'; import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service'; import { NotificationsService } from '../../notifications/notifications.service'; +import { SearchService } from '../../../+search-page/search-service/search.service'; +import { RequestService } from '../../../core/data/request.service'; /** * This component represents actions related to WorkflowItem object. @@ -30,12 +32,16 @@ export class WorkflowitemActionsComponent extends MyDSpaceActionsComponent; @@ -28,6 +32,10 @@ const mockDataService = jasmine.createSpyObj('WorkspaceitemDataService', { delete: jasmine.createSpy('delete') }); +const searchService = getMockSearchService(); + +const requestServce = getMockRequestService(); + const item = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: { @@ -62,6 +70,7 @@ mockObject = Object.assign(new WorkspaceItem(), { item: observableOf(rd), id: '1 describe('WorkspaceitemActionsComponent', () => { beforeEach(async(() => { + TestBed.configureTestingModule({ imports: [ NgbModule.forRoot(), @@ -78,6 +87,8 @@ describe('WorkspaceitemActionsComponent', () => { { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: Router, useValue: new RouterStub() }, { provide: WorkspaceitemDataService, useValue: mockDataService }, + { provide: SearchService, useValue: searchService }, + { provide: RequestService, useValue: requestServce }, NgbModal ], schemas: [NO_ERRORS_SCHEMA] @@ -161,4 +172,14 @@ describe('WorkspaceitemActionsComponent', () => { expect(notificationsServiceStub.error).toHaveBeenCalled(); }); })); + + it('should clear the object cache by href', async(() => { + component.reload(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(searchService.getEndpoint).toHaveBeenCalled(); + expect(requestServce.removeByHrefSubstring).toHaveBeenCalledWith('discover/search/objects'); + }); + })); }); diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts index ca0b416b60..0a5e18ff3d 100644 --- a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts +++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts @@ -1,4 +1,4 @@ -import { Component, Injector, Input } from '@angular/core'; +import { Component, Injector, Input, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; @@ -9,6 +9,8 @@ import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.mod import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service'; import { NotificationsService } from '../../notifications/notifications.service'; +import { SearchService } from '../../../+search-page/search-service/search.service'; +import { RequestService } from '../../../core/data/request.service'; /** * This component represents actions related to WorkspaceItem object. @@ -39,13 +41,17 @@ export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent { const config = MOCK_SUBMISSION_CONFIG; @@ -349,7 +353,12 @@ describe('SubmissionService test suite', () => { let scheduler: TestScheduler; let service: SubmissionService; + const searchService = getMockSearchService(); + + const requestServce = getMockRequestService(); + beforeEach(async(() => { + TestBed.configureTestingModule({ imports: [ StoreModule.forRoot({ submissionReducers } as any), @@ -365,6 +374,8 @@ describe('SubmissionService test suite', () => { { provide: Router, useValue: router }, { provide: SubmissionRestService, useValue: restService }, { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, + { provide: SearchService, useValue: searchService }, + { provide: RequestService, useValue: requestServce }, NotificationsService, RouteService, SubmissionService, diff --git a/src/app/submission/submission.service.ts b/src/app/submission/submission.service.ts index 7605f1c73a..c9be658b31 100644 --- a/src/app/submission/submission.service.ts +++ b/src/app/submission/submission.service.ts @@ -4,13 +4,12 @@ import { Router } from '@angular/router'; import { Observable, of as observableOf, Subscription, timer as observableTimer } from 'rxjs'; import { - catchError, + catchError, concatMap, distinctUntilChanged, filter, find, - first, map, - startWith + startWith, take, tap } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { TranslateService } from '@ngx-translate/core'; @@ -56,6 +55,8 @@ import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../shared/testing/utils'; +import { SearchService } from '../+search-page/search-service/search.service'; +import { RequestService } from '../core/data/request.service'; /** * A service that provides methods used in submission process. @@ -82,6 +83,8 @@ export class SubmissionService { * @param {RouteService} routeService * @param {Store} store * @param {TranslateService} translate + * @param {SearchService} searchService + * @param {RequestService} requestService */ constructor(@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig, protected notificationsService: NotificationsService, @@ -89,7 +92,9 @@ export class SubmissionService { protected router: Router, protected routeService: RouteService, protected store: Store, - protected translate: TranslateService) { + protected translate: TranslateService, + protected searchService: SearchService, + protected requestService: RequestService) { } /** @@ -460,16 +465,23 @@ export class SubmissionService { * Redirect to MyDspace page */ redirectToMyDSpace() { - this.routeService.getPreviousUrl().pipe( - first() - ).subscribe((previousUrl: string) => { - if (isEmpty(previousUrl) || !previousUrl.startsWith('/mydspace')) { - this.router.navigate(['/mydspace']); - } else { - this.router.navigateByUrl(previousUrl); - } - }); - + // This assures that the cache is empty before redirecting to mydspace. + // See https://github.com/DSpace/dspace-angular/pull/468 + this.searchService.getEndpoint().pipe( + take(1), + tap((url) => this.requestService.removeByHrefSubstring(url)), + // Now, do redirect. + concatMap( + () => this.routeService.getPreviousUrl().pipe( + take(1), + tap((previousUrl) => { + if (isEmpty(previousUrl) || !previousUrl.startsWith('/mydspace')) { + this.router.navigate(['/mydspace']); + } else { + this.router.navigateByUrl(previousUrl); + } + }))) + ).subscribe(); } /**