mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 02:24:11 +00:00
intermediate commit
This commit is contained in:
@@ -17,6 +17,7 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [fadeInOut]
|
||||
})
|
||||
|
||||
export class TopLevelCommunityListComponent {
|
||||
communitiesRDObs: Observable<RemoteData<PaginatedList<Community>>>;
|
||||
config: PaginationComponentOptions;
|
||||
|
@@ -8,21 +8,18 @@ import { SearchService } from './search.service';
|
||||
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
||||
import { ActivatedRoute, Router, UrlTree } from '@angular/router';
|
||||
import { RequestService } from '../../core/data/request.service';
|
||||
import { ResponseCacheService } from '../../core/cache/response-cache.service';
|
||||
import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
|
||||
import { RouterStub } from '../../shared/testing/router-stub';
|
||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
||||
import { Observable, combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { ResponseCacheEntry } from '../../core/cache/response-cache.reducer';
|
||||
import { RequestEntry } from '../../core/data/request.reducer';
|
||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||
import { getMockResponseCacheService } from '../../shared/mocks/mock-response-cache.service';
|
||||
import {
|
||||
FacetConfigSuccessResponse,
|
||||
SearchSuccessResponse
|
||||
} from '../../core/cache/response-cache.models';
|
||||
} from '../../core/cache/response.models';
|
||||
import { SearchQueryResponse } from './search-query-response.model';
|
||||
import { SearchFilterConfig } from './search-filter-config.model';
|
||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||
@@ -54,7 +51,6 @@ describe('SearchService', () => {
|
||||
providers: [
|
||||
{ provide: Router, useValue: router },
|
||||
{ provide: ActivatedRoute, useValue: route },
|
||||
{ provide: ResponseCacheService, useValue: getMockResponseCacheService() },
|
||||
{ provide: RequestService, useValue: getMockRequestService() },
|
||||
{ provide: RemoteDataBuildService, useValue: {} },
|
||||
{ provide: HALEndpointService, useValue: {} },
|
||||
@@ -86,9 +82,8 @@ describe('SearchService', () => {
|
||||
};
|
||||
|
||||
const remoteDataBuildService = {
|
||||
toRemoteDataObservable: (requestEntryObs: Observable<RequestEntry>, responseCacheObs: Observable<ResponseCacheEntry>, payloadObs: Observable<any>) => {
|
||||
return observableCombineLatest(requestEntryObs,
|
||||
responseCacheObs, payloadObs).pipe(
|
||||
toRemoteDataObservable: (requestEntryObs: Observable<RequestEntry>, payloadObs: Observable<any>) => {
|
||||
return observableCombineLatest(requestEntryObs, payloadObs).pipe(
|
||||
map(([req, res, pay]) => {
|
||||
return { req, res, pay };
|
||||
})
|
||||
@@ -113,7 +108,6 @@ describe('SearchService', () => {
|
||||
providers: [
|
||||
{ provide: Router, useValue: router },
|
||||
{ provide: ActivatedRoute, useValue: route },
|
||||
{ provide: ResponseCacheService, useValue: getMockResponseCacheService() },
|
||||
{ provide: RequestService, useValue: getMockRequestService() },
|
||||
{ provide: RemoteDataBuildService, useValue: remoteDataBuildService },
|
||||
{ provide: HALEndpointService, useValue: halService },
|
||||
@@ -162,10 +156,8 @@ describe('SearchService', () => {
|
||||
const searchOptions = new PaginatedSearchOptions({});
|
||||
const queryResponse = Object.assign(new SearchQueryResponse(), { objects: [] });
|
||||
const response = new SearchSuccessResponse(queryResponse, '200');
|
||||
const responseEntry = Object.assign(new ResponseCacheEntry(), { response: response });
|
||||
beforeEach(() => {
|
||||
spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
|
||||
(searchService as any).responseCache.get.and.returnValue(observableOf(responseEntry));
|
||||
/* tslint:disable:no-empty */
|
||||
searchService.search(searchOptions).subscribe((t) => {
|
||||
}); // subscribe to make sure all methods are called
|
||||
@@ -192,10 +184,8 @@ describe('SearchService', () => {
|
||||
const endPoint = 'http://endpoint.com/test/config';
|
||||
const filterConfig = [new SearchFilterConfig()];
|
||||
const response = new FacetConfigSuccessResponse(filterConfig, '200');
|
||||
const responseEntry = Object.assign(new ResponseCacheEntry(), { response: response });
|
||||
beforeEach(() => {
|
||||
spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
|
||||
(searchService as any).responseCache.get.and.returnValue(observableOf(responseEntry));
|
||||
/* tslint:disable:no-empty */
|
||||
searchService.getConfig(null).subscribe((t) => {
|
||||
}); // subscribe to make sure all methods are called
|
||||
@@ -224,10 +214,8 @@ describe('SearchService', () => {
|
||||
const requestUrl = endPoint + '?scope=' + scope;
|
||||
const filterConfig = [new SearchFilterConfig()];
|
||||
const response = new FacetConfigSuccessResponse(filterConfig, '200');
|
||||
const responseEntry = Object.assign(new ResponseCacheEntry(), { response: response });
|
||||
beforeEach(() => {
|
||||
spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
|
||||
(searchService as any).responseCache.get.and.returnValue(observableOf(responseEntry));
|
||||
/* tslint:disable:no-empty */
|
||||
searchService.getConfig(scope).subscribe((t) => {
|
||||
}); // subscribe to make sure all methods are called
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { of as observableOf, combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
@@ -7,15 +7,13 @@ import {
|
||||
Router,
|
||||
UrlSegmentGroup
|
||||
} from '@angular/router';
|
||||
import { flatMap, map, switchMap } from 'rxjs/operators';
|
||||
import { filter, flatMap, map, switchMap } from 'rxjs/operators';
|
||||
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
||||
import {
|
||||
FacetConfigSuccessResponse,
|
||||
FacetValueSuccessResponse,
|
||||
SearchSuccessResponse
|
||||
} from '../../core/cache/response-cache.models';
|
||||
import { ResponseCacheEntry } from '../../core/cache/response-cache.reducer';
|
||||
import { ResponseCacheService } from '../../core/cache/response-cache.service';
|
||||
} from '../../core/cache/response.models';
|
||||
import { PaginatedList } from '../../core/data/paginated-list';
|
||||
import { ResponseParsingService } from '../../core/data/parsing.service';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
@@ -24,7 +22,11 @@ import { RequestService } from '../../core/data/request.service';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
||||
import { configureRequest, getSucceededRemoteData } from '../../core/shared/operators';
|
||||
import {
|
||||
configureRequest,
|
||||
getResponseFromEntry,
|
||||
getSucceededRemoteData
|
||||
} from '../../core/shared/operators';
|
||||
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||
import { NormalizedSearchResult } from '../normalized-search-result.model';
|
||||
@@ -45,6 +47,7 @@ import { CommunityDataService } from '../../core/data/community-data.service';
|
||||
import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
import { ResourceType } from '../../core/shared/resource-type';
|
||||
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||
import { RequestEntry } from '../../core/data/request.reducer';
|
||||
|
||||
/**
|
||||
* Service that performs all general actions that have to do with the search page
|
||||
@@ -68,7 +71,6 @@ export class SearchService implements OnDestroy {
|
||||
|
||||
constructor(private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
private rdb: RemoteDataBuildService,
|
||||
private halService: HALEndpointService,
|
||||
@@ -101,13 +103,9 @@ export class SearchService implements OnDestroy {
|
||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||
);
|
||||
|
||||
const responseCacheObs = requestObs.pipe(
|
||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
||||
);
|
||||
|
||||
// get search results from response cache
|
||||
const sqrObs: Observable<SearchQueryResponse> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const sqrObs: Observable<SearchQueryResponse> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: SearchSuccessResponse) => response.results)
|
||||
);
|
||||
|
||||
@@ -139,8 +137,8 @@ export class SearchService implements OnDestroy {
|
||||
})
|
||||
);
|
||||
|
||||
const pageInfoObs: Observable<PageInfo> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const pageInfoObs: Observable<PageInfo> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: FacetValueSuccessResponse) => response.pageInfo)
|
||||
);
|
||||
|
||||
@@ -150,7 +148,7 @@ export class SearchService implements OnDestroy {
|
||||
})
|
||||
);
|
||||
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs);
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,18 +183,14 @@ export class SearchService implements OnDestroy {
|
||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||
);
|
||||
|
||||
const responseCacheObs = requestObs.pipe(
|
||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
||||
);
|
||||
|
||||
// get search results from response cache
|
||||
const facetConfigObs: Observable<SearchFilterConfig[]> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const facetConfigObs: Observable<SearchFilterConfig[]> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: FacetConfigSuccessResponse) =>
|
||||
response.results.map((result: any) => Object.assign(new SearchFilterConfig(), result)))
|
||||
);
|
||||
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, facetConfigObs);
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, facetConfigObs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,18 +226,14 @@ export class SearchService implements OnDestroy {
|
||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||
);
|
||||
|
||||
const responseCacheObs = requestObs.pipe(
|
||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
||||
);
|
||||
|
||||
// get search results from response cache
|
||||
const facetValueObs: Observable<FacetValue[]> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const facetValueObs: Observable<FacetValue[]> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: FacetValueSuccessResponse) => response.results)
|
||||
);
|
||||
|
||||
const pageInfoObs: Observable<PageInfo> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const pageInfoObs: Observable<PageInfo> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: FacetValueSuccessResponse) => response.pageInfo)
|
||||
);
|
||||
|
||||
@@ -253,7 +243,7 @@ export class SearchService implements OnDestroy {
|
||||
})
|
||||
);
|
||||
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs);
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -2,15 +2,15 @@ import { Observable, of as observableOf, throwError as observableThrowError } fr
|
||||
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { GLOBAL_CONFIG } from '../../../config';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { AuthGetRequest, AuthPostRequest, PostRequest, RestRequest } from '../data/request.models';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { AuthStatusResponse, ErrorResponse } from '../cache/response-cache.models';
|
||||
import { AuthStatusResponse, ErrorResponse } from '../cache/response.models';
|
||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||
import { RequestEntry } from '../data/request.reducer';
|
||||
import { getResponseFromEntry } from '../shared/operators';
|
||||
|
||||
@Injectable()
|
||||
export class AuthRequestService {
|
||||
@@ -19,15 +19,14 @@ export class AuthRequestService {
|
||||
|
||||
constructor(@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||
protected halService: HALEndpointService,
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService) {
|
||||
}
|
||||
|
||||
protected fetchRequest(request: RestRequest): Observable<any> {
|
||||
return this.responseCache.get(request.href).pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
return this.requestService.getByHref(request.href).pipe(
|
||||
getResponseFromEntry(),
|
||||
// TODO to review when https://github.com/DSpace/dspace-angular/issues/217 will be fixed
|
||||
tap(() => this.responseCache.remove(request.href)),
|
||||
// tap(() => this.responseCache.remove(request.href)),
|
||||
mergeMap((response) => {
|
||||
if (response.isSuccessful && isNotEmpty(response)) {
|
||||
return observableOf((response as AuthStatusResponse).response);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { AuthStatusResponse } from '../cache/response-cache.models';
|
||||
import { AuthStatusResponse } from '../cache/response.models';
|
||||
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
|
@@ -2,7 +2,7 @@ import { Inject, Injectable } from '@angular/core';
|
||||
|
||||
import { AuthObjectFactory } from './auth-object-factory';
|
||||
import { BaseResponseParsingService } from '../data/base-response-parsing.service';
|
||||
import { AuthStatusResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { AuthStatusResponse, RestResponse } from '../cache/response.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { GLOBAL_CONFIG } from '../../../config';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
|
@@ -2,10 +2,8 @@ import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
import { getMockRemoteDataBuildService } from '../../shared/mocks/mock-remote-data-build.service';
|
||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||
import { getMockResponseCacheService } from '../../shared/mocks/mock-response-cache.service';
|
||||
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { BrowseEndpointRequest, BrowseEntriesRequest, BrowseItemsRequest } from '../data/request.models';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||
@@ -14,7 +12,6 @@ import { BrowseService } from './browse.service';
|
||||
describe('BrowseService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let service: BrowseService;
|
||||
let responseCache: ResponseCacheService;
|
||||
let requestService: RequestService;
|
||||
let rdbService: RemoteDataBuildService;
|
||||
|
||||
@@ -79,22 +76,10 @@ describe('BrowseService', () => {
|
||||
})
|
||||
];
|
||||
|
||||
function initMockResponseCacheService(isSuccessful: boolean) {
|
||||
const rcs = getMockResponseCacheService();
|
||||
(rcs.get as any).and.returnValue(cold('b-', {
|
||||
b: {
|
||||
response: {
|
||||
isSuccessful,
|
||||
payload: browseDefinitions,
|
||||
}
|
||||
}
|
||||
}));
|
||||
return rcs;
|
||||
}
|
||||
|
||||
|
||||
function initTestService() {
|
||||
return new BrowseService(
|
||||
responseCache,
|
||||
requestService,
|
||||
halService,
|
||||
rdbService
|
||||
@@ -108,7 +93,6 @@ describe('BrowseService', () => {
|
||||
describe('getBrowseDefinitions', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
responseCache = initMockResponseCacheService(true);
|
||||
requestService = getMockRequestService();
|
||||
rdbService = getMockRemoteDataBuildService();
|
||||
service = initTestService();
|
||||
@@ -147,7 +131,6 @@ describe('BrowseService', () => {
|
||||
const mockAuthorName = 'Donald Smith';
|
||||
|
||||
beforeEach(() => {
|
||||
responseCache = initMockResponseCacheService(true);
|
||||
requestService = getMockRequestService();
|
||||
rdbService = getMockRemoteDataBuildService();
|
||||
service = initTestService();
|
||||
@@ -221,7 +204,6 @@ describe('BrowseService', () => {
|
||||
|
||||
describe('if getBrowseDefinitions fires', () => {
|
||||
beforeEach(() => {
|
||||
responseCache = initMockResponseCacheService(true);
|
||||
requestService = getMockRequestService();
|
||||
rdbService = getMockRemoteDataBuildService();
|
||||
service = initTestService();
|
||||
@@ -277,7 +259,6 @@ describe('BrowseService', () => {
|
||||
|
||||
describe('if getBrowseDefinitions doesn\'t fire', () => {
|
||||
it('should return undefined', () => {
|
||||
responseCache = initMockResponseCacheService(true);
|
||||
requestService = getMockRequestService();
|
||||
rdbService = getMockRemoteDataBuildService();
|
||||
service = initTestService();
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, map, startWith } from 'rxjs/operators';
|
||||
import { distinctUntilChanged, filter, map, startWith, tap } from 'rxjs/operators';
|
||||
import {
|
||||
ensureArrayHasValue,
|
||||
ensureArrayHasValue, hasValue,
|
||||
hasValueOperator,
|
||||
isEmpty,
|
||||
isNotEmpty,
|
||||
@@ -11,16 +11,13 @@ import {
|
||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { SortOptions } from '../cache/models/sort-options.model';
|
||||
import { GenericSuccessResponse } from '../cache/response-cache.models';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { GenericSuccessResponse } from '../cache/response.models';
|
||||
import { PaginatedList } from '../data/paginated-list';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import {
|
||||
BrowseEndpointRequest,
|
||||
BrowseEntriesRequest,
|
||||
BrowseItemsRequest,
|
||||
GetRequest,
|
||||
RestRequest
|
||||
} from '../data/request.models';
|
||||
import { RequestService } from '../data/request.service';
|
||||
@@ -29,14 +26,15 @@ import { BrowseEntry } from '../shared/browse-entry.model';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import {
|
||||
configureRequest,
|
||||
filterSuccessfulResponses, getBrowseDefinitionLinks,
|
||||
filterSuccessfulResponses,
|
||||
getBrowseDefinitionLinks,
|
||||
getRemoteDataPayload,
|
||||
getRequestFromSelflink,
|
||||
getResponseFromSelflink
|
||||
getRequestFromSelflink
|
||||
} from '../shared/operators';
|
||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||
import { Item } from '../shared/item.model';
|
||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||
import { RequestEntry } from '../data/request.reducer';
|
||||
|
||||
@Injectable()
|
||||
export class BrowseService {
|
||||
@@ -56,7 +54,6 @@ export class BrowseService {
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected halService: HALEndpointService,
|
||||
private rdb: RemoteDataBuildService,
|
||||
@@ -73,10 +70,8 @@ export class BrowseService {
|
||||
|
||||
const href$ = request$.pipe(map((request: RestRequest) => request.href));
|
||||
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
||||
const responseCache$ = href$.pipe(getResponseFromSelflink(this.responseCache));
|
||||
const payload$ = responseCache$.pipe(
|
||||
const payload$ = requestEntry$.pipe(
|
||||
filterSuccessfulResponses(),
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
map((response: GenericSuccessResponse<BrowseDefinition[]>) => response.payload),
|
||||
ensureArrayHasValue(),
|
||||
map((definitions: BrowseDefinition[]) => definitions
|
||||
@@ -84,7 +79,7 @@ export class BrowseService {
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
return this.rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$);
|
||||
return this.rdb.toRemoteDataObservable(requestEntry$, payload$);
|
||||
}
|
||||
|
||||
getBrowseEntriesFor(definitionID: string, options: {
|
||||
@@ -118,11 +113,9 @@ export class BrowseService {
|
||||
const href$ = request$.pipe(map((request: RestRequest) => request.href));
|
||||
|
||||
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
||||
const responseCache$ = href$.pipe(getResponseFromSelflink(this.responseCache));
|
||||
|
||||
const payload$ = responseCache$.pipe(
|
||||
const payload$ = requestEntry$.pipe(
|
||||
filterSuccessfulResponses(),
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
map((response: GenericSuccessResponse<BrowseEntry[]>) => new PaginatedList(response.pageInfo, response.payload)),
|
||||
map((list: PaginatedList<BrowseEntry>) => Object.assign(list, {
|
||||
page: list.page ? list.page.map((entry: BrowseEntry) => Object.assign(new BrowseEntry(), entry)) : list.page
|
||||
@@ -130,7 +123,7 @@ export class BrowseService {
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
return this.rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$);
|
||||
return this.rdb.toRemoteDataObservable(requestEntry$, payload$);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,11 +168,9 @@ export class BrowseService {
|
||||
const href$ = request$.pipe(map((request: RestRequest) => request.href));
|
||||
|
||||
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
||||
const responseCache$ = href$.pipe(getResponseFromSelflink(this.responseCache));
|
||||
|
||||
const payload$ = responseCache$.pipe(
|
||||
const payload$ = requestEntry$.pipe(
|
||||
filterSuccessfulResponses(),
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
map((response: GenericSuccessResponse<Item[]>) => new PaginatedList(response.pageInfo, response.payload)),
|
||||
map((list: PaginatedList<Item>) => Object.assign(list, {
|
||||
page: list.page ? list.page.map((item: DSpaceObject) => Object.assign(new Item(), item)) : list.page
|
||||
@@ -187,7 +178,7 @@ export class BrowseService {
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
return this.rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$);
|
||||
return this.rdb.toRemoteDataObservable(requestEntry$, payload$);
|
||||
}
|
||||
|
||||
getBrowseURLFor(metadatumKey: string, linkPath: string): Observable<string> {
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
combineLatest as observableCombineLatest,
|
||||
of as observableOf,
|
||||
Observable,
|
||||
of as observableOf,
|
||||
race as observableRace
|
||||
} from 'rxjs';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { distinctUntilChanged, flatMap, map, startWith } from 'rxjs/operators';
|
||||
import { distinctUntilChanged, flatMap, map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { hasValue, hasValueOperator, isEmpty, isNotEmpty } from '../../../shared/empty.util';
|
||||
import { PaginatedList } from '../../data/paginated-list';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
@@ -16,22 +16,18 @@ import { RequestService } from '../../data/request.service';
|
||||
|
||||
import { NormalizedObject } from '../models/normalized-object.model';
|
||||
import { ObjectCacheService } from '../object-cache.service';
|
||||
import { DSOSuccessResponse, ErrorResponse } from '../response-cache.models';
|
||||
import { ResponseCacheEntry } from '../response-cache.reducer';
|
||||
import { ResponseCacheService } from '../response-cache.service';
|
||||
import { DSOSuccessResponse, ErrorResponse } from '../response.models';
|
||||
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
|
||||
import { PageInfo } from '../../shared/page-info.model';
|
||||
import {
|
||||
filterSuccessfulResponses,
|
||||
getRequestFromSelflink,
|
||||
getResourceLinksFromResponse,
|
||||
getResponseFromSelflink,
|
||||
filterSuccessfulResponses
|
||||
getResourceLinksFromResponse
|
||||
} from '../../shared/operators';
|
||||
|
||||
@Injectable()
|
||||
export class RemoteDataBuildService {
|
||||
constructor(protected objectCache: ObjectCacheService,
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService) {
|
||||
}
|
||||
|
||||
@@ -39,19 +35,16 @@ export class RemoteDataBuildService {
|
||||
if (typeof href$ === 'string') {
|
||||
href$ = observableOf(href$);
|
||||
}
|
||||
const requestHref$ = href$.pipe(flatMap((href: string) =>
|
||||
this.objectCache.getRequestHrefBySelfLink(href)));
|
||||
const requestHref$ = href$.pipe(
|
||||
switchMap((href: string) =>
|
||||
this.objectCache.getRequestHrefBySelfLink(href)),
|
||||
);
|
||||
|
||||
const requestEntry$ = observableRace(
|
||||
href$.pipe(getRequestFromSelflink(this.requestService)),
|
||||
requestHref$.pipe(getRequestFromSelflink(this.requestService))
|
||||
);
|
||||
|
||||
const responseCache$ = observableRace(
|
||||
href$.pipe(getResponseFromSelflink(this.responseCache)),
|
||||
requestHref$.pipe(getResponseFromSelflink(this.responseCache))
|
||||
);
|
||||
|
||||
// always use self link if that is cached, only if it isn't, get it via the response.
|
||||
const payload$ =
|
||||
observableCombineLatest(
|
||||
@@ -59,7 +52,7 @@ export class RemoteDataBuildService {
|
||||
flatMap((href: string) => this.objectCache.getBySelfLink<TNormalized>(href)),
|
||||
startWith(undefined)
|
||||
),
|
||||
responseCache$.pipe(
|
||||
requestEntry$.pipe(
|
||||
getResourceLinksFromResponse(),
|
||||
flatMap((resourceSelfLinks: string[]) => {
|
||||
if (isNotEmpty(resourceSelfLinks)) {
|
||||
@@ -86,21 +79,21 @@ export class RemoteDataBuildService {
|
||||
startWith(undefined),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
return this.toRemoteDataObservable(requestEntry$, responseCache$, payload$);
|
||||
return this.toRemoteDataObservable(requestEntry$, payload$);
|
||||
}
|
||||
|
||||
toRemoteDataObservable<T>(requestEntry$: Observable<RequestEntry>, responseCache$: Observable<ResponseCacheEntry>, payload$: Observable<T>) {
|
||||
return observableCombineLatest(requestEntry$, responseCache$.pipe(startWith(undefined)), payload$).pipe(
|
||||
map(([reqEntry, resEntry, payload]) => {
|
||||
toRemoteDataObservable<T>(requestEntry$: Observable<RequestEntry>, payload$: Observable<T>) {
|
||||
return observableCombineLatest(requestEntry$, requestEntry$.pipe(startWith(undefined)), payload$).pipe(
|
||||
map(([reqEntry, payload]) => {
|
||||
const requestPending = hasValue(reqEntry.requestPending) ? reqEntry.requestPending : true;
|
||||
const responsePending = hasValue(reqEntry.responsePending) ? reqEntry.responsePending : false;
|
||||
let isSuccessful: boolean;
|
||||
let error: RemoteDataError;
|
||||
if (hasValue(resEntry) && hasValue(resEntry.response)) {
|
||||
isSuccessful = resEntry.response.isSuccessful;
|
||||
const errorMessage = isSuccessful === false ? (resEntry.response as ErrorResponse).errorMessage : undefined;
|
||||
if (hasValue(reqEntry) && hasValue(reqEntry.response)) {
|
||||
isSuccessful = reqEntry.response.isSuccessful;
|
||||
const errorMessage = isSuccessful === false ? (reqEntry.response as ErrorResponse).errorMessage : undefined;
|
||||
if (hasValue(errorMessage)) {
|
||||
error = new RemoteDataError(resEntry.response.statusCode, errorMessage);
|
||||
error = new RemoteDataError(reqEntry.response.statusCode, errorMessage);
|
||||
}
|
||||
}
|
||||
return new RemoteData(
|
||||
@@ -120,9 +113,7 @@ export class RemoteDataBuildService {
|
||||
}
|
||||
|
||||
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
||||
const responseCache$ = href$.pipe(getResponseFromSelflink(this.responseCache));
|
||||
|
||||
const tDomainList$ = responseCache$.pipe(
|
||||
const tDomainList$ = requestEntry$.pipe(
|
||||
getResourceLinksFromResponse(),
|
||||
flatMap((resourceUUIDs: string[]) => {
|
||||
return this.objectCache.getList(resourceUUIDs).pipe(
|
||||
@@ -135,12 +126,12 @@ export class RemoteDataBuildService {
|
||||
startWith([]),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
const pageInfo$ = responseCache$.pipe(
|
||||
// tDomainList$.subscribe((t) => {console.log('domainlist', t)});
|
||||
const pageInfo$ = requestEntry$.pipe(
|
||||
filterSuccessfulResponses(),
|
||||
map((entry: ResponseCacheEntry) => {
|
||||
if (hasValue((entry.response as DSOSuccessResponse).pageInfo)) {
|
||||
const resPageInfo = (entry.response as DSOSuccessResponse).pageInfo;
|
||||
map((response: DSOSuccessResponse) => {
|
||||
if (hasValue((response as DSOSuccessResponse).pageInfo)) {
|
||||
const resPageInfo = (response as DSOSuccessResponse).pageInfo;
|
||||
if (isNotEmpty(resPageInfo) && resPageInfo.currentPage >= 0) {
|
||||
return Object.assign({}, resPageInfo, { currentPage: resPageInfo.currentPage + 1 });
|
||||
} else {
|
||||
@@ -156,7 +147,7 @@ export class RemoteDataBuildService {
|
||||
})
|
||||
);
|
||||
|
||||
return this.toRemoteDataObservable(requestEntry$, responseCache$, payload$);
|
||||
return this.toRemoteDataObservable(requestEntry$, payload$);
|
||||
}
|
||||
|
||||
build<TNormalized, TDomain>(normalized: TNormalized): TDomain {
|
||||
@@ -204,8 +195,9 @@ export class RemoteDataBuildService {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const domainModel = getMapsTo(normalized.constructor);
|
||||
// console.log('domain model', normalized);
|
||||
|
||||
return Object.assign(new domainModel(), normalized, links);
|
||||
}
|
||||
|
||||
|
72
src/app/core/cache/response-cache.actions.ts
vendored
72
src/app/core/cache/response-cache.actions.ts
vendored
@@ -1,72 +0,0 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
|
||||
import { type } from '../../shared/ngrx/type';
|
||||
import { RestResponse } from './response-cache.models';
|
||||
|
||||
/**
|
||||
* The list of ResponseCacheAction type definitions
|
||||
*/
|
||||
export const ResponseCacheActionTypes = {
|
||||
ADD: type('dspace/core/cache/response/ADD'),
|
||||
REMOVE: type('dspace/core/cache/response/REMOVE'),
|
||||
RESET_TIMESTAMPS: type('dspace/core/cache/response/RESET_TIMESTAMPS')
|
||||
};
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
export class ResponseCacheAddAction implements Action {
|
||||
type = ResponseCacheActionTypes.ADD;
|
||||
payload: {
|
||||
key: string,
|
||||
response: RestResponse
|
||||
timeAdded: number;
|
||||
msToLive: number;
|
||||
};
|
||||
|
||||
constructor(key: string, response: RestResponse, timeAdded: number, msToLive: number) {
|
||||
this.payload = { key, response, timeAdded, msToLive };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An ngrx action to remove a request from the cache
|
||||
*/
|
||||
export class ResponseCacheRemoveAction implements Action {
|
||||
type = ResponseCacheActionTypes.REMOVE;
|
||||
payload: string;
|
||||
|
||||
/**
|
||||
* Create a new ResponseCacheRemoveAction
|
||||
* @param key
|
||||
* The key of the request to remove
|
||||
*/
|
||||
constructor(key: string) {
|
||||
this.payload = key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An ngrx action to reset the timeAdded property of all cached objects
|
||||
*/
|
||||
export class ResetResponseCacheTimestampsAction implements Action {
|
||||
type = ResponseCacheActionTypes.RESET_TIMESTAMPS;
|
||||
payload: number;
|
||||
|
||||
/**
|
||||
* Create a new ResetObjectCacheTimestampsAction
|
||||
*
|
||||
* @param newTimestamp
|
||||
* the new timeAdded all objects should get
|
||||
*/
|
||||
constructor(newTimestamp: number) {
|
||||
this.payload = newTimestamp;
|
||||
}
|
||||
}
|
||||
/* tslint:enable:max-classes-per-file */
|
||||
|
||||
/**
|
||||
* A type to encompass all ResponseCacheActions
|
||||
*/
|
||||
export type ResponseCacheAction
|
||||
= ResponseCacheAddAction
|
||||
| ResponseCacheRemoveAction
|
||||
| ResetResponseCacheTimestampsAction;
|
@@ -1,38 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { Observable } from 'rxjs';
|
||||
import { provideMockActions } from '@ngrx/effects/testing';
|
||||
import { cold, hot } from 'jasmine-marbles';
|
||||
import { StoreActionTypes } from '../../store.actions';
|
||||
import { ResponseCacheEffects } from './response-cache.effects';
|
||||
import { ResetResponseCacheTimestampsAction } from './response-cache.actions';
|
||||
|
||||
describe('ResponseCacheEffects', () => {
|
||||
let cacheEffects: ResponseCacheEffects;
|
||||
let actions: Observable<any>;
|
||||
const timestamp = 10000;
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ResponseCacheEffects,
|
||||
provideMockActions(() => actions),
|
||||
// other providers
|
||||
],
|
||||
});
|
||||
|
||||
cacheEffects = TestBed.get(ResponseCacheEffects);
|
||||
});
|
||||
|
||||
describe('fixTimestampsOnRehydrate$', () => {
|
||||
|
||||
it('should return a RESET_TIMESTAMPS action in response to a REHYDRATE action', () => {
|
||||
spyOn(Date.prototype, 'getTime').and.callFake(() => {
|
||||
return timestamp;
|
||||
});
|
||||
actions = hot('--a-', { a: { type: StoreActionTypes.REHYDRATE, payload: {} } });
|
||||
|
||||
const expected = cold('--b-', { b: new ResetResponseCacheTimestampsAction(new Date().getTime()) });
|
||||
|
||||
expect(cacheEffects.fixTimestampsOnRehydrate).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
});
|
27
src/app/core/cache/response-cache.effects.ts
vendored
27
src/app/core/cache/response-cache.effects.ts
vendored
@@ -1,27 +0,0 @@
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Actions, Effect, ofType } from '@ngrx/effects';
|
||||
|
||||
import { ResetResponseCacheTimestampsAction } from './response-cache.actions';
|
||||
import { StoreActionTypes } from '../../store.actions';
|
||||
|
||||
@Injectable()
|
||||
export class ResponseCacheEffects {
|
||||
|
||||
/**
|
||||
* When the store is rehydrated in the browser, set all cache
|
||||
* timestamps to 'now', because the time zone of the server can
|
||||
* differ from the client.
|
||||
*
|
||||
* This assumes that the server cached everything a negligible
|
||||
* time ago, and will likely need to be revisited later
|
||||
*/
|
||||
@Effect() fixTimestampsOnRehydrate = this.actions$
|
||||
.pipe(ofType(StoreActionTypes.REHYDRATE),
|
||||
map(() => new ResetResponseCacheTimestampsAction(new Date().getTime()))
|
||||
);
|
||||
|
||||
constructor(private actions$: Actions,) {
|
||||
}
|
||||
|
||||
}
|
124
src/app/core/cache/response-cache.reducer.spec.ts
vendored
124
src/app/core/cache/response-cache.reducer.spec.ts
vendored
@@ -1,124 +0,0 @@
|
||||
import * as deepFreeze from 'deep-freeze';
|
||||
|
||||
import { responseCacheReducer, ResponseCacheState } from './response-cache.reducer';
|
||||
|
||||
import {
|
||||
ResponseCacheRemoveAction,
|
||||
ResetResponseCacheTimestampsAction, ResponseCacheAddAction
|
||||
} from './response-cache.actions';
|
||||
import { RestResponse } from './response-cache.models';
|
||||
|
||||
class NullAction extends ResponseCacheRemoveAction {
|
||||
type = null;
|
||||
payload = null;
|
||||
|
||||
constructor() {
|
||||
super(null);
|
||||
}
|
||||
}
|
||||
|
||||
describe('responseCacheReducer', () => {
|
||||
const keys = ['125c17f89046283c5f0640722aac9feb', 'a06c3006a41caec5d635af099b0c780c'];
|
||||
const msToLive = 900000;
|
||||
const uuids = [
|
||||
'9e32a2e2-6b91-4236-a361-995ccdc14c60',
|
||||
'598ce822-c357-46f3-ab70-63724d02d6ad',
|
||||
'be8325f7-243b-49f4-8a4b-df2b793ff3b5'
|
||||
];
|
||||
const testState: ResponseCacheState = {
|
||||
[keys[0]]: {
|
||||
key: keys[0],
|
||||
response: new RestResponse(true, '200'),
|
||||
timeAdded: new Date().getTime(),
|
||||
msToLive: msToLive
|
||||
},
|
||||
[keys[1]]: {
|
||||
key: keys[1],
|
||||
response: new RestResponse(true, '200'),
|
||||
timeAdded: new Date().getTime(),
|
||||
msToLive: msToLive
|
||||
}
|
||||
};
|
||||
deepFreeze(testState);
|
||||
const errorState: {} = {
|
||||
[keys[0]]: {
|
||||
errorMessage: 'error',
|
||||
resourceUUIDs: uuids
|
||||
}
|
||||
};
|
||||
deepFreeze(errorState);
|
||||
|
||||
it('should return the current state when no valid actions have been made', () => {
|
||||
const action = new NullAction();
|
||||
const newState = responseCacheReducer(testState, action);
|
||||
|
||||
expect(newState).toEqual(testState);
|
||||
});
|
||||
|
||||
it('should start with an empty cache', () => {
|
||||
const action = new NullAction();
|
||||
const initialState = responseCacheReducer(undefined, action);
|
||||
|
||||
expect(initialState).toEqual(Object.create(null));
|
||||
});
|
||||
|
||||
describe('ADD', () => {
|
||||
const addTimeAdded = new Date().getTime();
|
||||
const addMsToLive = 5;
|
||||
const addResponse = new RestResponse(true, '200');
|
||||
const action = new ResponseCacheAddAction(keys[0], addResponse, addTimeAdded, addMsToLive);
|
||||
|
||||
it('should perform the action without affecting the previous state', () => {
|
||||
// testState has already been frozen above
|
||||
responseCacheReducer(testState, action);
|
||||
});
|
||||
|
||||
it('should add the response to the cached request', () => {
|
||||
const newState = responseCacheReducer(testState, action);
|
||||
expect(newState[keys[0]].timeAdded).toBe(addTimeAdded);
|
||||
expect(newState[keys[0]].msToLive).toBe(addMsToLive);
|
||||
expect(newState[keys[0]].response).toBe(addResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe('REMOVE', () => {
|
||||
it('should perform the action without affecting the previous state', () => {
|
||||
const action = new ResponseCacheRemoveAction(keys[0]);
|
||||
// testState has already been frozen above
|
||||
responseCacheReducer(testState, action);
|
||||
});
|
||||
|
||||
it('should remove the specified request from the cache', () => {
|
||||
const action = new ResponseCacheRemoveAction(keys[0]);
|
||||
const newState = responseCacheReducer(testState, action);
|
||||
expect(testState[keys[0]]).not.toBeUndefined();
|
||||
expect(newState[keys[0]]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('shouldn\'t do anything when the specified key isn\'t cached', () => {
|
||||
const wrongKey = 'this isn\'t cached';
|
||||
const action = new ResponseCacheRemoveAction(wrongKey);
|
||||
const newState = responseCacheReducer(testState, action);
|
||||
expect(testState[wrongKey]).toBeUndefined();
|
||||
expect(newState).toEqual(testState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RESET_TIMESTAMPS', () => {
|
||||
const newTimeStamp = new Date().getTime();
|
||||
const action = new ResetResponseCacheTimestampsAction(newTimeStamp);
|
||||
|
||||
it('should perform the action without affecting the previous state', () => {
|
||||
// testState has already been frozen above
|
||||
responseCacheReducer(testState, action);
|
||||
});
|
||||
|
||||
it('should set the timestamp of all requests in the cache', () => {
|
||||
const newState = responseCacheReducer(testState, action);
|
||||
Object.keys(newState).forEach((key) => {
|
||||
expect(newState[key].timeAdded).toEqual(newTimeStamp);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
111
src/app/core/cache/response-cache.reducer.ts
vendored
111
src/app/core/cache/response-cache.reducer.ts
vendored
@@ -1,111 +0,0 @@
|
||||
import {
|
||||
ResponseCacheAction, ResponseCacheActionTypes,
|
||||
ResponseCacheRemoveAction, ResetResponseCacheTimestampsAction,
|
||||
ResponseCacheAddAction
|
||||
} from './response-cache.actions';
|
||||
import { CacheEntry } from './cache-entry';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { RestResponse } from './response-cache.models';
|
||||
|
||||
/**
|
||||
* An entry in the ResponseCache
|
||||
*/
|
||||
export class ResponseCacheEntry implements CacheEntry {
|
||||
key: string;
|
||||
response: RestResponse;
|
||||
timeAdded: number;
|
||||
msToLive: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ResponseCache State
|
||||
*/
|
||||
export interface ResponseCacheState {
|
||||
[key: string]: ResponseCacheEntry
|
||||
}
|
||||
|
||||
// Object.create(null) ensures the object has no default js properties (e.g. `__proto__`)
|
||||
const initialState = Object.create(null);
|
||||
|
||||
/**
|
||||
* The ResponseCache Reducer
|
||||
*
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* the action to perform on the state
|
||||
* @return ResponseCacheState
|
||||
* the new state
|
||||
*/
|
||||
export function responseCacheReducer(state = initialState, action: ResponseCacheAction): ResponseCacheState {
|
||||
switch (action.type) {
|
||||
|
||||
case ResponseCacheActionTypes.ADD: {
|
||||
return addToCache(state, action as ResponseCacheAddAction);
|
||||
}
|
||||
|
||||
case ResponseCacheActionTypes.REMOVE: {
|
||||
return removeFromCache(state, action as ResponseCacheRemoveAction);
|
||||
}
|
||||
|
||||
case ResponseCacheActionTypes.RESET_TIMESTAMPS: {
|
||||
return resetResponseCacheTimestamps(state, action as ResetResponseCacheTimestampsAction)
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addToCache(state: ResponseCacheState, action: ResponseCacheAddAction): ResponseCacheState {
|
||||
return Object.assign({}, state, {
|
||||
[action.payload.key]: {
|
||||
key: action.payload.key,
|
||||
response: action.payload.response,
|
||||
timeAdded: action.payload.timeAdded,
|
||||
msToLive: action.payload.msToLive
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a request from the cache
|
||||
*
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* an ResponseCacheRemoveAction
|
||||
* @return ResponseCacheState
|
||||
* the new state, with the request removed if it existed.
|
||||
*/
|
||||
function removeFromCache(state: ResponseCacheState, action: ResponseCacheRemoveAction): ResponseCacheState {
|
||||
if (hasValue(state[action.payload])) {
|
||||
const newCache = Object.assign({}, state);
|
||||
delete newCache[action.payload];
|
||||
|
||||
return newCache;
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeAdded timestamp of every cached request to the specified value
|
||||
*
|
||||
* @param state
|
||||
* the current state
|
||||
* @param action
|
||||
* a ResetResponseCacheTimestampsAction
|
||||
* @return ResponseCacheState
|
||||
* the new state, with all timeAdded timestamps set to the specified value
|
||||
*/
|
||||
function resetResponseCacheTimestamps(state: ResponseCacheState, action: ResetResponseCacheTimestampsAction): ResponseCacheState {
|
||||
const newState = Object.create(null);
|
||||
Object.keys(state).forEach((key) => {
|
||||
newState[key] = Object.assign({}, state[key], {
|
||||
timeAdded: action.payload
|
||||
});
|
||||
});
|
||||
return newState;
|
||||
}
|
100
src/app/core/cache/response-cache.service.spec.ts
vendored
100
src/app/core/cache/response-cache.service.spec.ts
vendored
@@ -1,100 +0,0 @@
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { ResponseCacheService } from './response-cache.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { RestResponse } from './response-cache.models';
|
||||
import { ResponseCacheEntry } from './response-cache.reducer';
|
||||
import { first } from 'rxjs/operators';
|
||||
import * as ngrx from '@ngrx/store'
|
||||
import { cold } from 'jasmine-marbles';
|
||||
|
||||
describe('ResponseCacheService', () => {
|
||||
let service: ResponseCacheService;
|
||||
let store: Store<CoreState>;
|
||||
|
||||
const keys = ['125c17f89046283c5f0640722aac9feb', 'a06c3006a41caec5d635af099b0c780c'];
|
||||
const timestamp = new Date().getTime();
|
||||
const validCacheEntry = (key) => {
|
||||
return {
|
||||
key: key,
|
||||
response: new RestResponse(true, '200'),
|
||||
timeAdded: timestamp,
|
||||
msToLive: 24 * 60 * 60 * 1000 // a day
|
||||
}
|
||||
};
|
||||
const invalidCacheEntry = (key) => {
|
||||
return {
|
||||
key: key,
|
||||
response: new RestResponse(true, '200'),
|
||||
timeAdded: 0,
|
||||
msToLive: 0
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
store = new Store<CoreState>(undefined, undefined, undefined);
|
||||
spyOn(store, 'dispatch');
|
||||
service = new ResponseCacheService(store);
|
||||
spyOn(Date.prototype, 'getTime').and.callFake(() => {
|
||||
return timestamp;
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', () => {
|
||||
it('should return an observable of the cached request with the specified key', () => {
|
||||
spyOnProperty(ngrx, 'select').and.callFake(() => {
|
||||
return () => {
|
||||
return () => observableOf(validCacheEntry(keys[1]));
|
||||
};
|
||||
});
|
||||
let testObj: ResponseCacheEntry;
|
||||
service.get(keys[1]).pipe(first()).subscribe((entry) => {
|
||||
testObj = entry;
|
||||
});
|
||||
expect(testObj.key).toEqual(keys[1]);
|
||||
});
|
||||
|
||||
it('should not return a cached request that has exceeded its time to live', () => {
|
||||
spyOnProperty(ngrx, 'select').and.callFake(() => {
|
||||
return () => {
|
||||
return () => observableOf(invalidCacheEntry(keys[1]));
|
||||
};
|
||||
});
|
||||
|
||||
let getObsHasFired = false;
|
||||
const subscription = service.get(keys[1]).subscribe((entry) => getObsHasFired = true);
|
||||
expect(getObsHasFired).toBe(false);
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
});
|
||||
|
||||
describe('has', () => {
|
||||
it('should return true if the request with the supplied key is cached and still valid', () => {
|
||||
spyOnProperty(ngrx, 'select').and.callFake(() => {
|
||||
return () => {
|
||||
return () => observableOf(validCacheEntry(keys[1]));
|
||||
};
|
||||
});
|
||||
expect(service.has(keys[1])).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the request with the supplied key isn\'t cached', () => {
|
||||
spyOnProperty(ngrx, 'select').and.callFake(() => {
|
||||
return () => {
|
||||
return () => observableOf(undefined);
|
||||
};
|
||||
});
|
||||
expect(service.has(keys[1])).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if the request with the supplied key is cached but has exceeded its time to live', () => {
|
||||
spyOnProperty(ngrx, 'select').and.callFake(() => {
|
||||
return () => {
|
||||
return () => observableOf(invalidCacheEntry(keys[1]));
|
||||
};
|
||||
});
|
||||
expect(service.has(keys[1])).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
100
src/app/core/cache/response-cache.service.ts
vendored
100
src/app/core/cache/response-cache.service.ts
vendored
@@ -1,100 +0,0 @@
|
||||
import { filter, take, distinctUntilChanged, first } from 'rxjs/operators';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { ResponseCacheEntry } from './response-cache.reducer';
|
||||
import { hasNoValue } from '../../shared/empty.util';
|
||||
import { ResponseCacheRemoveAction, ResponseCacheAddAction } from './response-cache.actions';
|
||||
import { RestResponse } from './response-cache.models';
|
||||
import { coreSelector, CoreState } from '../core.reducers';
|
||||
import { pathSelector } from '../shared/selectors';
|
||||
|
||||
function entryFromKeySelector(key: string): MemoizedSelector<CoreState, ResponseCacheEntry> {
|
||||
return pathSelector<CoreState, ResponseCacheEntry>(coreSelector, 'cache/response', key);
|
||||
}
|
||||
|
||||
/**
|
||||
* A service to interact with the response cache
|
||||
*/
|
||||
@Injectable()
|
||||
export class ResponseCacheService {
|
||||
constructor(
|
||||
private store: Store<CoreState>
|
||||
) {
|
||||
}
|
||||
|
||||
add(key: string, response: RestResponse, msToLive: number): Observable<ResponseCacheEntry> {
|
||||
if (!this.has(key)) {
|
||||
this.store.dispatch(new ResponseCacheAddAction(key, response, new Date().getTime(), msToLive));
|
||||
}
|
||||
return this.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an observable of the response with the specified key
|
||||
*
|
||||
* @param key
|
||||
* the key of the response to get
|
||||
* @return Observable<ResponseCacheEntry>
|
||||
* an observable of the ResponseCacheEntry with the specified key
|
||||
*/
|
||||
get(key: string): Observable<ResponseCacheEntry> {
|
||||
return this.store.pipe(
|
||||
select(entryFromKeySelector(key)),
|
||||
filter((entry: ResponseCacheEntry) => this.isValid(entry)),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the response with the specified key is cached
|
||||
*
|
||||
* @param key
|
||||
* the key of the response to check
|
||||
* @return boolean
|
||||
* true if the response with the specified key is cached,
|
||||
* false otherwise
|
||||
*/
|
||||
has(key: string): boolean {
|
||||
let result: boolean;
|
||||
|
||||
this.store.pipe(select(entryFromKeySelector(key)),
|
||||
first()
|
||||
).subscribe((entry: ResponseCacheEntry) => {
|
||||
result = this.isValid(entry);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
remove(key: string): void {
|
||||
if (this.has(key)) {
|
||||
this.store.dispatch(new ResponseCacheRemoveAction(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a ResponseCacheEntry should still be cached
|
||||
*
|
||||
* @param entry
|
||||
* the entry to check
|
||||
* @return boolean
|
||||
* false if the entry is null, undefined, or its time to
|
||||
* live has been exceeded, true otherwise
|
||||
*/
|
||||
private isValid(entry: ResponseCacheEntry): boolean {
|
||||
if (hasNoValue(entry)) {
|
||||
return false;
|
||||
} else {
|
||||
const timeOutdated = entry.timeAdded + entry.msToLive;
|
||||
const isOutDated = new Date().getTime() > timeOutdated;
|
||||
if (isOutDated) {
|
||||
this.store.dispatch(new ResponseCacheRemoveAction(entry.key));
|
||||
}
|
||||
return !isOutDated;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -13,7 +13,7 @@ import { AuthStatus } from '../auth/models/auth-status.model';
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
export class RestResponse {
|
||||
public toCache = true;
|
||||
public timeAdded: number;
|
||||
|
||||
constructor(
|
||||
public isSuccessful: boolean,
|
@@ -1,7 +1,6 @@
|
||||
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { ConfigService } from './config.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { ConfigRequest, FindAllOptions } from '../data/request.models';
|
||||
@@ -16,7 +15,6 @@ class TestService extends ConfigService {
|
||||
protected browseEndpoint = BROWSE;
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected halService: HALEndpointService) {
|
||||
super();
|
||||
@@ -26,7 +24,6 @@ class TestService extends ConfigService {
|
||||
describe('ConfigService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let service: TestService;
|
||||
let responseCache: ResponseCacheService;
|
||||
let requestService: RequestService;
|
||||
let halService: any;
|
||||
|
||||
@@ -39,17 +36,9 @@ describe('ConfigService', () => {
|
||||
const scopedEndpoint = `${serviceEndpoint}/${scopeName}`;
|
||||
const searchEndpoint = `${serviceEndpoint}/${BROWSE}?uuid=${scopeID}`;
|
||||
|
||||
function initMockResponseCacheService(isSuccessful: boolean): ResponseCacheService {
|
||||
return jasmine.createSpyObj('responseCache', {
|
||||
get: cold('c-', {
|
||||
c: { response: { isSuccessful } }
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function initTestService(): TestService {
|
||||
return new TestService(
|
||||
responseCache,
|
||||
requestService,
|
||||
halService
|
||||
);
|
||||
@@ -57,7 +46,6 @@ describe('ConfigService', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
scheduler = getTestScheduler();
|
||||
responseCache = initMockResponseCacheService(true);
|
||||
requestService = getMockRequestService();
|
||||
halService = new HALEndpointServiceStub(configEndpoint);
|
||||
service = initTestService();
|
||||
|
@@ -1,24 +1,25 @@
|
||||
import { Observable, of as observableOf, throwError as observableThrowError, merge as observableMerge } from 'rxjs';
|
||||
import { merge as observableMerge, Observable, throwError as observableThrowError } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { ConfigSuccessResponse } from '../cache/response-cache.models';
|
||||
import { ConfigSuccessResponse } from '../cache/response.models';
|
||||
import { ConfigRequest, FindAllOptions, RestRequest } from '../data/request.models';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { ConfigData } from './config-data';
|
||||
import { RequestEntry } from '../data/request.reducer';
|
||||
import { getResponseFromEntry } from '../shared/operators';
|
||||
|
||||
export abstract class ConfigService {
|
||||
protected request: ConfigRequest;
|
||||
protected abstract responseCache: ResponseCacheService;
|
||||
protected abstract requestService: RequestService;
|
||||
protected abstract linkPath: string;
|
||||
protected abstract browseEndpoint: string;
|
||||
protected abstract halService: HALEndpointService;
|
||||
|
||||
protected getConfig(request: RestRequest): Observable<ConfigData> {
|
||||
const responses = this.responseCache.get(request.href).pipe(map((entry: ResponseCacheEntry) => entry.response));
|
||||
const responses = this.requestService.getByHref(request.href).pipe(
|
||||
getResponseFromEntry()
|
||||
);
|
||||
const errorResponses = responses.pipe(
|
||||
filter((response) => !response.isSuccessful),
|
||||
mergeMap(() => observableThrowError(new Error(`Couldn't retrieve the config`)))
|
||||
@@ -94,7 +95,6 @@ export abstract class ConfigService {
|
||||
}
|
||||
|
||||
public getConfigBySearch(options: FindAllOptions = {}): Observable<ConfigData> {
|
||||
console.log(this.halService.getEndpoint(this.linkPath));
|
||||
return this.halService.getEndpoint(this.linkPath).pipe(
|
||||
map((endpoint: string) => this.getConfigSearchHref(endpoint, options)),
|
||||
filter((href: string) => isNotEmpty(href)),
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { ConfigService } from './config.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
|
||||
@@ -11,7 +10,6 @@ export class SubmissionDefinitionsConfigService extends ConfigService {
|
||||
protected browseEndpoint = 'search/findByCollection';
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected halService: HALEndpointService) {
|
||||
super();
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { ConfigService } from './config.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
|
||||
@@ -11,7 +10,6 @@ export class SubmissionFormsConfigService extends ConfigService {
|
||||
protected browseEndpoint = '';
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected halService: HALEndpointService) {
|
||||
super();
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { ConfigService } from './config.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
|
||||
@@ -11,7 +10,6 @@ export class SubmissionSectionsConfigService extends ConfigService {
|
||||
protected browseEndpoint = '';
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected halService: HALEndpointService) {
|
||||
super();
|
||||
|
@@ -1,13 +1,11 @@
|
||||
|
||||
import { ObjectCacheEffects } from './cache/object-cache.effects';
|
||||
import { ResponseCacheEffects } from './cache/response-cache.effects';
|
||||
import { UUIDIndexEffects } from './index/index.effects';
|
||||
import { RequestEffects } from './data/request.effects';
|
||||
import { AuthEffects } from './auth/auth.effects';
|
||||
import { ServerSyncBufferEffects } from './cache/server-sync-buffer.effects';
|
||||
|
||||
export const coreEffects = [
|
||||
ResponseCacheEffects,
|
||||
RequestEffects,
|
||||
ObjectCacheEffects,
|
||||
UUIDIndexEffects,
|
||||
|
@@ -32,7 +32,6 @@ import { ObjectCacheService } from './cache/object-cache.service';
|
||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||
import { RemoteDataBuildService } from './cache/builders/remote-data-build.service';
|
||||
import { RequestService } from './data/request.service';
|
||||
import { ResponseCacheService } from './cache/response-cache.service';
|
||||
import { EndpointMapResponseParsingService } from './data/endpoint-map-response-parsing.service';
|
||||
import { ServerResponseService } from '../shared/services/server-response.service';
|
||||
import { NativeWindowFactory, NativeWindowService } from '../shared/services/window.service';
|
||||
@@ -102,7 +101,6 @@ const PROVIDERS = [
|
||||
RegistryService,
|
||||
RemoteDataBuildService,
|
||||
RequestService,
|
||||
ResponseCacheService,
|
||||
EndpointMapResponseParsingService,
|
||||
FacetValueResponseParsingService,
|
||||
FacetValueMapResponseParsingService,
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { ActionReducerMap, createFeatureSelector } from '@ngrx/store';
|
||||
|
||||
import { responseCacheReducer, ResponseCacheState } from './cache/response-cache.reducer';
|
||||
import { objectCacheReducer, ObjectCacheState } from './cache/object-cache.reducer';
|
||||
import { indexReducer, IndexState } from './index/index.reducer';
|
||||
import { requestReducer, RequestState } from './data/request.reducer';
|
||||
@@ -9,7 +8,6 @@ import { serverSyncBufferReducer, ServerSyncBufferState } from './cache/server-s
|
||||
|
||||
export interface CoreState {
|
||||
'cache/object': ObjectCacheState,
|
||||
'cache/response': ResponseCacheState,
|
||||
'cache/syncbuffer': ServerSyncBufferState,
|
||||
'data/request': RequestState,
|
||||
'index': IndexState,
|
||||
@@ -18,7 +16,6 @@ export interface CoreState {
|
||||
|
||||
export const coreReducers: ActionReducerMap<CoreState> = {
|
||||
'cache/object': objectCacheReducer,
|
||||
'cache/response': responseCacheReducer,
|
||||
'cache/syncbuffer': serverSyncBufferReducer,
|
||||
'data/request': requestReducer,
|
||||
'index': indexReducer,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { getMockObjectCacheService } from '../../shared/mocks/mock-object-cache.service';
|
||||
import { ErrorResponse, GenericSuccessResponse } from '../cache/response-cache.models';
|
||||
import { ErrorResponse, GenericSuccessResponse } from '../cache/response.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { BrowseEntriesResponseParsingService } from './browse-entries-response-parsing.service';
|
||||
import { BrowseEntriesRequest } from './request.models';
|
||||
|
@@ -7,7 +7,7 @@ import {
|
||||
ErrorResponse,
|
||||
GenericSuccessResponse,
|
||||
RestResponse
|
||||
} from '../cache/response-cache.models';
|
||||
} from '../cache/response.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { BrowseEntry } from '../shared/browse-entry.model';
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { getMockObjectCacheService } from '../../shared/mocks/mock-object-cache.service';
|
||||
import { ErrorResponse, GenericSuccessResponse } from '../cache/response-cache.models';
|
||||
import { ErrorResponse, GenericSuccessResponse } from '../cache/response.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { BrowseEntriesResponseParsingService } from './browse-entries-response-parsing.service';
|
||||
import { BrowseEntriesRequest, BrowseItemsRequest } from './request.models';
|
||||
|
@@ -7,7 +7,7 @@ import {
|
||||
ErrorResponse,
|
||||
GenericSuccessResponse,
|
||||
RestResponse
|
||||
} from '../cache/response-cache.models';
|
||||
} from '../cache/response.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { BaseResponseParsingService } from './base-response-parsing.service';
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { BrowseResponseParsingService } from './browse-response-parsing.service';
|
||||
import { BrowseEndpointRequest } from './request.models';
|
||||
import { GenericSuccessResponse, ErrorResponse } from '../cache/response-cache.models';
|
||||
import { GenericSuccessResponse, ErrorResponse } from '../cache/response.models';
|
||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
|
||||
|
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { GenericSuccessResponse, ErrorResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { GenericSuccessResponse, ErrorResponse, RestResponse } from '../cache/response.models';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||
|
@@ -1,10 +1,8 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { NormalizedCollection } from '../cache/models/normalized-collection.model';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { Collection } from '../shared/collection.model';
|
||||
import { ComColDataService } from './comcol-data.service';
|
||||
@@ -17,7 +15,6 @@ export class CollectionDataService extends ComColDataService<NormalizedCollectio
|
||||
protected linkPath = 'collections';
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
|
@@ -5,7 +5,6 @@ import { GlobalConfig } from '../../../config';
|
||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { ComColDataService } from './comcol-data.service';
|
||||
import { CommunityDataService } from './community-data.service';
|
||||
@@ -23,7 +22,6 @@ class NormalizedTestObject extends NormalizedObject {
|
||||
class TestService extends ComColDataService<NormalizedTestObject, any> {
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
@@ -41,7 +39,6 @@ class TestService extends ComColDataService<NormalizedTestObject, any> {
|
||||
describe('ComColDataService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let service: TestService;
|
||||
let responseCache: ResponseCacheService;
|
||||
let requestService: RequestService;
|
||||
let cds: CommunityDataService;
|
||||
let objectCache: ObjectCacheService;
|
||||
@@ -68,14 +65,6 @@ describe('ComColDataService', () => {
|
||||
});
|
||||
}
|
||||
|
||||
function initMockResponseCacheService(isSuccessful: boolean): ResponseCacheService {
|
||||
return jasmine.createSpyObj('responseCache', {
|
||||
get: cold('c-', {
|
||||
c: { response: { isSuccessful } }
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function initMockObjectCacheService(): ObjectCacheService {
|
||||
return jasmine.createSpyObj('objectCache', {
|
||||
getByUUID: cold('d-', {
|
||||
@@ -90,7 +79,6 @@ describe('ComColDataService', () => {
|
||||
|
||||
function initTestService(): TestService {
|
||||
return new TestService(
|
||||
responseCache,
|
||||
requestService,
|
||||
rdbService,
|
||||
store,
|
||||
@@ -111,7 +99,6 @@ describe('ComColDataService', () => {
|
||||
cds = initMockCommunityDataService();
|
||||
requestService = getMockRequestService();
|
||||
objectCache = initMockObjectCacheService();
|
||||
responseCache = initMockResponseCacheService(true);
|
||||
service = initTestService();
|
||||
|
||||
const expected = new FindByIDRequest(requestService.generateRequestId(), communityEndpoint, scopeID);
|
||||
@@ -127,7 +114,6 @@ describe('ComColDataService', () => {
|
||||
cds = initMockCommunityDataService();
|
||||
requestService = getMockRequestService();
|
||||
objectCache = initMockObjectCacheService();
|
||||
responseCache = initMockResponseCacheService(true);
|
||||
service = initTestService();
|
||||
});
|
||||
|
||||
@@ -150,7 +136,6 @@ describe('ComColDataService', () => {
|
||||
cds = initMockCommunityDataService();
|
||||
requestService = getMockRequestService();
|
||||
objectCache = initMockObjectCacheService();
|
||||
responseCache = initMockResponseCacheService(false);
|
||||
service = initTestService();
|
||||
});
|
||||
|
||||
|
@@ -1,15 +1,16 @@
|
||||
import { distinctUntilChanged, filter, map, mergeMap, take, tap } from 'rxjs/operators';
|
||||
import { distinctUntilChanged, filter, map, mergeMap, share, take, tap } from 'rxjs/operators';
|
||||
import { merge as observableMerge, Observable, throwError as observableThrowError } from 'rxjs';
|
||||
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||
import { NormalizedCommunity } from '../cache/models/normalized-community.model';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { CommunityDataService } from './community-data.service';
|
||||
|
||||
import { DataService } from './data.service';
|
||||
import { FindAllOptions, FindByIDRequest } from './request.models';
|
||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RequestEntry } from './request.reducer';
|
||||
import { getResponseFromEntry } from '../shared/operators';
|
||||
|
||||
export abstract class ComColDataService<TNormalized extends NormalizedObject, TDomain> extends DataService<TNormalized, TDomain> {
|
||||
protected abstract cds: CommunityDataService;
|
||||
@@ -26,9 +27,9 @@ export abstract class ComColDataService<TNormalized extends NormalizedObject, TD
|
||||
* @return { Observable<string> }
|
||||
* an Observable<string> containing the scoped URL
|
||||
*/
|
||||
public getBrowseEndpoint(options: FindAllOptions = {}): Observable<string> {
|
||||
public getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
|
||||
if (isEmpty(options.scopeID)) {
|
||||
return this.halService.getEndpoint(this.linkPath);
|
||||
return this.halService.getEndpoint(linkPath);
|
||||
} else {
|
||||
const scopeCommunityHrefObs = this.cds.getEndpoint().pipe(
|
||||
mergeMap((endpoint: string) => this.cds.getFindByIDHref(endpoint, options.scopeID)),
|
||||
@@ -37,7 +38,7 @@ export abstract class ComColDataService<TNormalized extends NormalizedObject, TD
|
||||
tap((href: string) => {
|
||||
const request = new FindByIDRequest(this.requestService.generateRequestId(), href, options.scopeID);
|
||||
this.requestService.configure(request);
|
||||
}),);
|
||||
}));
|
||||
|
||||
// return scopeCommunityHrefObs.pipe(
|
||||
// mergeMap((href: string) => this.responseCache.get(href)),
|
||||
@@ -46,7 +47,7 @@ export abstract class ComColDataService<TNormalized extends NormalizedObject, TD
|
||||
// if (response.isSuccessful) {
|
||||
// const community$: Observable<NormalizedCommunity> = this.objectCache.getByUUID(scopeID);
|
||||
// return community$.pipe(
|
||||
// map((community) => community._links[this.linkPath]),
|
||||
// map((community) => community._links[linkPath]),
|
||||
// filter((href) => isNotEmpty(href)),
|
||||
// distinctUntilChanged()
|
||||
// );
|
||||
@@ -57,8 +58,8 @@ export abstract class ComColDataService<TNormalized extends NormalizedObject, TD
|
||||
// distinctUntilChanged()
|
||||
// );
|
||||
const responses = scopeCommunityHrefObs.pipe(
|
||||
mergeMap((href: string) => this.responseCache.get(href)),
|
||||
map((entry: ResponseCacheEntry) => entry.response));
|
||||
mergeMap((href: string) => this.requestService.getByHref(href)),
|
||||
getResponseFromEntry());
|
||||
const errorResponses = responses.pipe(
|
||||
filter((response) => !response.isSuccessful),
|
||||
mergeMap(() => observableThrowError(new Error(`The Community with scope ${options.scopeID} couldn't be retrieved`)))
|
||||
@@ -66,11 +67,11 @@ export abstract class ComColDataService<TNormalized extends NormalizedObject, TD
|
||||
const successResponses = responses.pipe(
|
||||
filter((response) => response.isSuccessful),
|
||||
mergeMap(() => this.objectCache.getByUUID(options.scopeID)),
|
||||
map((nc: NormalizedCommunity) => nc._links[this.linkPath]),
|
||||
map((nc: NormalizedCommunity) => nc._links[linkPath]),
|
||||
filter((href) => isNotEmpty(href))
|
||||
);
|
||||
|
||||
return observableMerge(errorResponses, successResponses).pipe(distinctUntilChanged());
|
||||
return observableMerge(errorResponses, successResponses).pipe(distinctUntilChanged(), share());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,10 @@
|
||||
|
||||
import {mergeMap, filter, take} from 'rxjs/operators';
|
||||
import { filter, mergeMap, take } from 'rxjs/operators';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { NormalizedCommunity } from '../cache/models/normalized-community.model';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { Community } from '../shared/community.model';
|
||||
import { ComColDataService } from './comcol-data.service';
|
||||
@@ -25,7 +23,6 @@ export class CommunityDataService extends ComColDataService<NormalizedCommunity,
|
||||
protected cds = this;
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
@@ -40,12 +37,10 @@ export class CommunityDataService extends ComColDataService<NormalizedCommunity,
|
||||
}
|
||||
|
||||
findTop(options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<Community>>> {
|
||||
const hrefObs = this.halService.getEndpoint(this.topLinkPath).pipe(filter((href: string) => isNotEmpty(href)),
|
||||
mergeMap((endpoint: string) => this.getFindAllHref(options)),);
|
||||
|
||||
const hrefObs = this.getFindAllHref(options, this.topLinkPath);
|
||||
hrefObs.pipe(
|
||||
filter((href: string) => hasValue(href)),
|
||||
take(1),)
|
||||
take(1))
|
||||
.subscribe((href: string) => {
|
||||
const request = new FindAllRequest(this.requestService.generateRequestId(), href, options);
|
||||
this.requestService.configure(request);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { ConfigSuccessResponse, ErrorResponse } from '../cache/response-cache.models';
|
||||
import { ConfigSuccessResponse, ErrorResponse } from '../cache/response.models';
|
||||
import { ConfigResponseParsingService } from './config-response-parsing.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
|
@@ -3,7 +3,7 @@ import { Inject, Injectable } from '@angular/core';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { ConfigSuccessResponse, ErrorResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { ConfigSuccessResponse, ErrorResponse, RestResponse } from '../cache/response.models';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { ConfigObjectFactory } from '../shared/config/config-object-factory';
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { DataService } from './data.service';
|
||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { RequestService } from './request.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
@@ -22,7 +21,6 @@ class NormalizedTestObject extends NormalizedObject {
|
||||
|
||||
class TestService extends DataService<NormalizedTestObject, any> {
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
@@ -33,7 +31,7 @@ class TestService extends DataService<NormalizedTestObject, any> {
|
||||
super();
|
||||
}
|
||||
|
||||
public getBrowseEndpoint(options: FindAllOptions): Observable<string> {
|
||||
public getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
|
||||
return observableOf(endpoint);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +39,6 @@ class TestService extends DataService<NormalizedTestObject, any> {
|
||||
describe('DataService', () => {
|
||||
let service: TestService;
|
||||
let options: FindAllOptions;
|
||||
const responseCache = {} as ResponseCacheService;
|
||||
const requestService = {} as RequestService;
|
||||
const halService = {} as HALEndpointService;
|
||||
const rdbService = {} as RemoteDataBuildService;
|
||||
@@ -57,7 +54,6 @@ describe('DataService', () => {
|
||||
|
||||
function initTestService(): TestService {
|
||||
return new TestService(
|
||||
responseCache,
|
||||
requestService,
|
||||
rdbService,
|
||||
store,
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import { distinctUntilChanged, filter, first, map, take } from 'rxjs/operators';
|
||||
import { delay, distinctUntilChanged, filter, first, map, take, tap } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||
@@ -15,9 +14,9 @@ import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||
import { compare, Operation } from 'fast-json-patch';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
|
||||
export abstract class DataService<TNormalized extends NormalizedObject, TDomain> {
|
||||
protected abstract responseCache: ResponseCacheService;
|
||||
protected abstract requestService: RequestService;
|
||||
protected abstract rdbService: RemoteDataBuildService;
|
||||
protected abstract store: Store<CoreState>;
|
||||
@@ -25,34 +24,31 @@ export abstract class DataService<TNormalized extends NormalizedObject, TDomain>
|
||||
protected abstract halService: HALEndpointService;
|
||||
protected abstract objectCache: ObjectCacheService;
|
||||
|
||||
public abstract getBrowseEndpoint(options: FindAllOptions): Observable<string>
|
||||
public abstract getBrowseEndpoint(options: FindAllOptions, linkPath?: string): Observable<string>
|
||||
|
||||
protected getFindAllHref(options: FindAllOptions = {}): Observable<string> {
|
||||
protected getFindAllHref(options: FindAllOptions = {}, linkPath?: string): Observable<string> {
|
||||
let result: Observable<string>;
|
||||
const args = [];
|
||||
|
||||
result = this.getBrowseEndpoint(options).pipe(distinctUntilChanged());
|
||||
|
||||
result = this.getBrowseEndpoint(options, linkPath);
|
||||
if (hasValue(options.currentPage) && typeof options.currentPage === 'number') {
|
||||
/* TODO: this is a temporary fix for the pagination start index (0 or 1) discrepancy between the rest and the frontend respectively */
|
||||
args.push(`page=${options.currentPage - 1}`);
|
||||
}
|
||||
|
||||
if (hasValue(options.elementsPerPage)) {
|
||||
args.push(`size=${options.elementsPerPage}`);
|
||||
}
|
||||
|
||||
if (hasValue(options.sort)) {
|
||||
args.push(`sort=${options.sort.field},${options.sort.direction}`);
|
||||
}
|
||||
|
||||
if (hasValue(options.startsWith)) {
|
||||
args.push(`startsWith=${options.startsWith}`);
|
||||
}
|
||||
|
||||
if (isNotEmpty(args)) {
|
||||
return result.pipe(map((href: string) => new URLCombiner(href, `?${args.join('&')}`).toString()));
|
||||
} else {
|
||||
result.subscribe((t) => console.log(t));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -115,6 +111,7 @@ export abstract class DataService<TNormalized extends NormalizedObject, TDomain>
|
||||
this.objectCache.addPatch(object.self, operations);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO implement, after the structure of the REST server's POST response is finalized
|
||||
// create(dso: DSpaceObject): Observable<RemoteData<TDomain>> {
|
||||
// const postHrefObs = this.getEndpoint();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { RestResponse } from '../cache/response-cache.models';
|
||||
import { RestResponse } from '../cache/response.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
|
@@ -7,7 +7,7 @@ import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||
import { ResourceType } from '../shared/resource-type';
|
||||
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { RestResponse, DSOSuccessResponse } from '../cache/response-cache.models';
|
||||
import { RestResponse, DSOSuccessResponse } from '../cache/response.models';
|
||||
import { RestRequest } from './request.models';
|
||||
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
@@ -23,12 +23,14 @@ export class DSOResponseParsingService extends BaseResponseParsingService implem
|
||||
constructor(
|
||||
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||
protected objectCache: ObjectCacheService,
|
||||
) { super();
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
||||
const processRequestDTO = this.process<NormalizedObject, ResourceType>(data.payload, request.href);
|
||||
let objectList = processRequestDTO;
|
||||
|
||||
if (hasNoValue(processRequestDTO)) {
|
||||
return new DSOSuccessResponse([], data.statusCode, undefined)
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { NormalizedDSpaceObject } from '../cache/models/normalized-dspace-object.model';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
@@ -18,7 +17,6 @@ class DataServiceImpl extends DataService<NormalizedDSpaceObject, DSpaceObject>
|
||||
protected linkPath = 'dso';
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
@@ -27,8 +25,8 @@ class DataServiceImpl extends DataService<NormalizedDSpaceObject, DSpaceObject>
|
||||
super();
|
||||
}
|
||||
|
||||
getBrowseEndpoint(options: FindAllOptions): Observable<string> {
|
||||
return this.halService.getEndpoint(this.linkPath);
|
||||
getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
|
||||
return this.halService.getEndpoint(linkPath);
|
||||
}
|
||||
|
||||
getFindByIDHref(endpoint, resourceID): string {
|
||||
@@ -46,7 +44,7 @@ export class DSpaceObjectDataService {
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected halService: HALEndpointService,
|
||||
protected objectCache: ObjectCacheService) {
|
||||
this.dataService = new DataServiceImpl(null, requestService, rdbService, null, halService, objectCache);
|
||||
this.dataService = new DataServiceImpl(requestService, rdbService, null, halService, objectCache);
|
||||
}
|
||||
|
||||
findById(uuid: string): Observable<RemoteData<DSpaceObject>> {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { GLOBAL_CONFIG } from '../../../config';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
import { ErrorResponse, RestResponse, EndpointMapSuccessResponse } from '../cache/response-cache.models';
|
||||
import { ErrorResponse, RestResponse, EndpointMapSuccessResponse } from '../cache/response.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
|
@@ -2,7 +2,7 @@ import { Inject, Injectable } from '@angular/core';
|
||||
import {
|
||||
FacetConfigSuccessResponse,
|
||||
RestResponse
|
||||
} from '../cache/response-cache.models';
|
||||
} from '../cache/response.models';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
|
@@ -4,7 +4,7 @@ import {
|
||||
FacetValueMapSuccessResponse,
|
||||
FacetValueSuccessResponse,
|
||||
RestResponse
|
||||
} from '../cache/response-cache.models';
|
||||
} from '../cache/response.models';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
|
@@ -4,7 +4,7 @@ import {
|
||||
FacetValueMapSuccessResponse,
|
||||
FacetValueSuccessResponse,
|
||||
RestResponse
|
||||
} from '../cache/response-cache.models';
|
||||
} from '../cache/response.models';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
|
@@ -3,7 +3,6 @@ import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
import { BrowseService } from '../browse/browse.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { ItemDataService } from './item-data.service';
|
||||
import { RequestService } from './request.service';
|
||||
@@ -16,7 +15,6 @@ describe('ItemDataService', () => {
|
||||
let service: ItemDataService;
|
||||
let bs: BrowseService;
|
||||
const requestService = {} as RequestService;
|
||||
const responseCache = {} as ResponseCacheService;
|
||||
const rdbService = {} as RemoteDataBuildService;
|
||||
const objectCache = {} as ObjectCacheService;
|
||||
const store = {} as Store<CoreState>;
|
||||
@@ -48,7 +46,6 @@ describe('ItemDataService', () => {
|
||||
|
||||
function initTestService() {
|
||||
return new ItemDataService(
|
||||
responseCache,
|
||||
requestService,
|
||||
rdbService,
|
||||
store,
|
||||
|
@@ -7,7 +7,6 @@ import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { BrowseService } from '../browse/browse.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { NormalizedItem } from '../cache/models/normalized-item.model';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { Item } from '../shared/item.model';
|
||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||
@@ -23,7 +22,6 @@ export class ItemDataService extends DataService<NormalizedItem, Item> {
|
||||
protected linkPath = 'items';
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
@@ -39,12 +37,12 @@ export class ItemDataService extends DataService<NormalizedItem, Item> {
|
||||
* @param {FindAllOptions} options
|
||||
* @returns {Observable<string>}
|
||||
*/
|
||||
public getBrowseEndpoint(options: FindAllOptions = {}): Observable<string> {
|
||||
public getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
|
||||
let field = 'dc.date.issued';
|
||||
if (options.sort && options.sort.field) {
|
||||
field = options.sort.field;
|
||||
}
|
||||
return this.bs.getBrowseURLFor(field, this.linkPath).pipe(
|
||||
return this.bs.getBrowseURLFor(field, linkPath).pipe(
|
||||
filter((href: string) => isNotEmpty(href)),
|
||||
map((href: string) => new URLCombiner(href, `?scope=${options.scopeID}`).toString()),
|
||||
distinctUntilChanged(),);
|
||||
|
@@ -4,7 +4,7 @@ import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.
|
||||
import { RestRequest } from './request.models';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MetadataschemaSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { MetadataschemaSuccessResponse, RestResponse } from '../cache/response.models';
|
||||
|
||||
@Injectable()
|
||||
export class MetadataschemaParsingService implements ResponseParsingService {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { RestRequest } from './request.models';
|
||||
import { RestResponse } from '../cache/response-cache.models';
|
||||
import { RestResponse } from '../cache/response.models';
|
||||
|
||||
export interface ResponseParsingService {
|
||||
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { RegistryBitstreamformatsSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { RegistryBitstreamformatsSuccessResponse, RestResponse } from '../cache/response.models';
|
||||
import { RegistryBitstreamformatsResponse } from '../registry/registry-bitstreamformats-response.model';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
RegistryMetadatafieldsSuccessResponse,
|
||||
RestResponse
|
||||
} from '../cache/response-cache.models';
|
||||
} from '../cache/response.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { RestRequest } from './request.models';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { RegistryMetadataschemasSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { RegistryMetadataschemasSuccessResponse, RestResponse } from '../cache/response.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { RestRequest } from './request.models';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { type } from '../../shared/ngrx/type';
|
||||
import { RestRequest } from './request.models';
|
||||
import { RestResponse } from '../cache/response.models';
|
||||
|
||||
/**
|
||||
* The list of RequestAction type definitions
|
||||
@@ -8,7 +9,8 @@ import { RestRequest } from './request.models';
|
||||
export const RequestActionTypes = {
|
||||
CONFIGURE: type('dspace/core/data/request/CONFIGURE'),
|
||||
EXECUTE: type('dspace/core/data/request/EXECUTE'),
|
||||
COMPLETE: type('dspace/core/data/request/COMPLETE')
|
||||
COMPLETE: type('dspace/core/data/request/COMPLETE'),
|
||||
RESET_TIMESTAMPS: type('dspace/core/data/request/RESET_TIMESTAMPS')
|
||||
};
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
@@ -43,7 +45,10 @@ export class RequestExecuteAction implements Action {
|
||||
*/
|
||||
export class RequestCompleteAction implements Action {
|
||||
type = RequestActionTypes.COMPLETE;
|
||||
payload: string;
|
||||
payload: {
|
||||
uuid: string,
|
||||
response: RestResponse
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new RequestCompleteAction
|
||||
@@ -51,10 +56,32 @@ export class RequestCompleteAction implements Action {
|
||||
* @param uuid
|
||||
* the request's uuid
|
||||
*/
|
||||
constructor(uuid: string) {
|
||||
this.payload = uuid;
|
||||
constructor(uuid: string, response: RestResponse) {
|
||||
this.payload = {
|
||||
uuid,
|
||||
response
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An ngrx action to reset the timeAdded property of all responses in the cached objects
|
||||
*/
|
||||
export class ResetResponseTimestampsAction implements Action {
|
||||
type = RequestActionTypes.RESET_TIMESTAMPS;
|
||||
payload: number;
|
||||
|
||||
/**
|
||||
* Create a new ResetResponseTimestampsAction
|
||||
*
|
||||
* @param newTimestamp
|
||||
* the new timeAdded all objects should get
|
||||
*/
|
||||
constructor(newTimestamp: number) {
|
||||
this.payload = newTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
/* tslint:enable:max-classes-per-file */
|
||||
|
||||
/**
|
||||
@@ -63,4 +90,5 @@ export class RequestCompleteAction implements Action {
|
||||
export type RequestAction
|
||||
= RequestConfigureAction
|
||||
| RequestExecuteAction
|
||||
| RequestCompleteAction;
|
||||
| RequestCompleteAction
|
||||
| ResetResponseTimestampsAction;
|
||||
|
@@ -1,30 +1,33 @@
|
||||
|
||||
import {of as observableOf, Observable } from 'rxjs';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { Inject, Injectable, Injector } from '@angular/core';
|
||||
import { Request } from '@angular/http';
|
||||
import { RequestArgs } from '@angular/http/src/interfaces';
|
||||
import { Actions, Effect, ofType } from '@ngrx/effects';
|
||||
|
||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { ErrorResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
|
||||
import { DSpaceRESTv2Service } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||
import { RequestActionTypes, RequestCompleteAction, RequestExecuteAction } from './request.actions';
|
||||
import {
|
||||
RequestActionTypes,
|
||||
RequestCompleteAction,
|
||||
RequestExecuteAction,
|
||||
ResetResponseTimestampsAction
|
||||
} from './request.actions';
|
||||
import { RequestError, RestRequest } from './request.models';
|
||||
import { RequestEntry } from './request.reducer';
|
||||
import { RequestService } from './request.service';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
|
||||
import { catchError, flatMap, map, take, tap } from 'rxjs/operators';
|
||||
import { ErrorResponse, RestResponse } from '../cache/response.models';
|
||||
import { StoreActionTypes } from '../../store.actions';
|
||||
|
||||
export const addToResponseCacheAndCompleteAction = (request: RestRequest, responseCache: ResponseCacheService, envConfig: GlobalConfig) =>
|
||||
(source: Observable<ErrorResponse>): Observable<RequestCompleteAction> =>
|
||||
export const addToResponseCacheAndCompleteAction = (request: RestRequest, envConfig: GlobalConfig) =>
|
||||
(source: Observable<RestResponse>): Observable<RequestCompleteAction> =>
|
||||
source.pipe(
|
||||
tap((response: RestResponse) => responseCache.add(request.href, response, request.responseMsToLive ? request.responseMsToLive : envConfig.cache.msToLive.default)),
|
||||
map((response: RestResponse) => new RequestCompleteAction(request.uuid))
|
||||
map((response: RestResponse) => {
|
||||
return new RequestCompleteAction(request.uuid, response)
|
||||
})
|
||||
);
|
||||
|
||||
@Injectable()
|
||||
@@ -46,20 +49,32 @@ export class RequestEffects {
|
||||
}
|
||||
return this.restApi.request(request.method, request.href, body, request.options).pipe(
|
||||
map((data: DSpaceRESTV2Response) => this.injector.get(request.getResponseParser()).parse(request, data)),
|
||||
addToResponseCacheAndCompleteAction(request, this.responseCache, this.EnvConfig),
|
||||
addToResponseCacheAndCompleteAction(request, this.EnvConfig),
|
||||
catchError((error: RequestError) => observableOf(new ErrorResponse(error)).pipe(
|
||||
addToResponseCacheAndCompleteAction(request, this.responseCache, this.EnvConfig)
|
||||
addToResponseCacheAndCompleteAction(request, this.EnvConfig)
|
||||
))
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* When the store is rehydrated in the browser, set all cache
|
||||
* timestamps to 'now', because the time zone of the server can
|
||||
* differ from the client.
|
||||
*
|
||||
* This assumes that the server cached everything a negligible
|
||||
* time ago, and will likely need to be revisited later
|
||||
*/
|
||||
@Effect() fixTimestampsOnRehydrate = this.actions$
|
||||
.pipe(ofType(StoreActionTypes.REHYDRATE),
|
||||
map(() => new ResetResponseTimestampsAction(new Date().getTime()))
|
||||
);
|
||||
|
||||
constructor(
|
||||
@Inject(GLOBAL_CONFIG) private EnvConfig: GlobalConfig,
|
||||
private actions$: Actions,
|
||||
private restApi: DSpaceRESTv2Service,
|
||||
private injector: Injector,
|
||||
private responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService
|
||||
) { }
|
||||
|
||||
|
@@ -14,32 +14,36 @@ import { BrowseItemsResponseParsingService } from './browse-items-response-parsi
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
|
||||
|
||||
export abstract class RestRequest {
|
||||
public responseMsToLive = 0;
|
||||
constructor(
|
||||
public uuid: string,
|
||||
public href: string,
|
||||
public method: RestRequestMethod = RestRequestMethod.GET,
|
||||
public body?: any,
|
||||
public options?: HttpOptions,
|
||||
public responseMsToLive?: number
|
||||
) {
|
||||
}
|
||||
|
||||
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
||||
return DSOResponseParsingService;
|
||||
}
|
||||
|
||||
get toCache(): boolean {
|
||||
return this.responseMsToLive > 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class GetRequest extends RestRequest {
|
||||
public responseMsToLive = 60 * 15 * 1000;
|
||||
|
||||
constructor(
|
||||
public uuid: string,
|
||||
public href: string,
|
||||
public body?: any,
|
||||
public options?: HttpOptions,
|
||||
public responseMsToLive?: number
|
||||
) {
|
||||
super(uuid, href, RestRequestMethod.GET, body, options, responseMsToLive)
|
||||
super(uuid, href, RestRequestMethod.GET, body, options)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +216,7 @@ export class IntegrationRequest extends GetRequest {
|
||||
return IntegrationResponseParsingService;
|
||||
}
|
||||
}
|
||||
|
||||
export class RequestError extends Error {
|
||||
statusText: string;
|
||||
}
|
||||
|
@@ -1,14 +1,16 @@
|
||||
import {
|
||||
RequestActionTypes, RequestAction, RequestConfigureAction,
|
||||
RequestExecuteAction, RequestCompleteAction
|
||||
RequestExecuteAction, RequestCompleteAction, ResetResponseTimestampsAction
|
||||
} from './request.actions';
|
||||
import { RestRequest } from './request.models';
|
||||
import { RestResponse } from '../cache/response.models';
|
||||
|
||||
export class RequestEntry {
|
||||
request: RestRequest;
|
||||
requestPending: boolean;
|
||||
responsePending: boolean;
|
||||
completed: boolean;
|
||||
response: RestResponse
|
||||
}
|
||||
|
||||
export interface RequestState {
|
||||
@@ -32,6 +34,9 @@ export function requestReducer(state = initialState, action: RequestAction): Req
|
||||
case RequestActionTypes.COMPLETE: {
|
||||
return completeRequest(state, action as RequestCompleteAction);
|
||||
}
|
||||
case RequestActionTypes.RESET_TIMESTAMPS: {
|
||||
return resetResponseTimestamps(state, action as ResetResponseTimestampsAction);
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
@@ -45,7 +50,7 @@ function configureRequest(state: RequestState, action: RequestConfigureAction):
|
||||
request: action.payload,
|
||||
requestPending: true,
|
||||
responsePending: false,
|
||||
completed: false
|
||||
completed: false,
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -70,10 +75,25 @@ function executeRequest(state: RequestState, action: RequestExecuteAction): Requ
|
||||
* the new state, with the response added to the request
|
||||
*/
|
||||
function completeRequest(state: RequestState, action: RequestCompleteAction): RequestState {
|
||||
return Object.assign({}, state, {
|
||||
[action.payload]: Object.assign({}, state[action.payload], {
|
||||
const time = new Date().getTime();
|
||||
|
||||
const ob = Object.assign({}, state, {
|
||||
[action.payload.uuid]: Object.assign({}, state[action.payload.uuid], {
|
||||
responsePending: false,
|
||||
completed: true
|
||||
completed: true,
|
||||
response: Object.assign({}, action.payload.response, { timeAdded: time })
|
||||
})
|
||||
});
|
||||
console.log(ob);
|
||||
return ob;
|
||||
}
|
||||
|
||||
function resetResponseTimestamps(state: RequestState, action: ResetResponseTimestampsAction) {
|
||||
const newState = Object.create(null);
|
||||
Object.keys(state).forEach((key) => {
|
||||
newState[key] = Object.assign({}, state[key],
|
||||
{ response: Object.assign({}, state[key].response, { timeAdded: action.payload }) }
|
||||
);
|
||||
});
|
||||
return newState;
|
||||
}
|
||||
|
@@ -1,10 +1,8 @@
|
||||
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { getMockObjectCacheService } from '../../shared/mocks/mock-object-cache.service';
|
||||
import { getMockResponseCacheService } from '../../shared/mocks/mock-response-cache.service';
|
||||
import { defaultUUID, getMockUUIDService } from '../../shared/mocks/mock-uuid.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { UUIDService } from '../shared/uuid.service';
|
||||
import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
|
||||
@@ -29,7 +27,6 @@ describe('RequestService', () => {
|
||||
let service: RequestService;
|
||||
let serviceAsAny: any;
|
||||
let objectCache: ObjectCacheService;
|
||||
let responseCache: ResponseCacheService;
|
||||
let uuidService: UUIDService;
|
||||
let store: Store<CoreState>;
|
||||
|
||||
@@ -49,7 +46,6 @@ describe('RequestService', () => {
|
||||
objectCache = getMockObjectCacheService();
|
||||
(objectCache.hasBySelfLink as any).and.returnValue(false);
|
||||
|
||||
responseCache = getMockResponseCacheService();
|
||||
(responseCache.has as any).and.returnValue(false);
|
||||
(responseCache.get as any).and.returnValue(observableOf(undefined));
|
||||
|
||||
@@ -65,7 +61,6 @@ describe('RequestService', () => {
|
||||
|
||||
service = new RequestService(
|
||||
objectCache,
|
||||
responseCache,
|
||||
uuidService,
|
||||
store
|
||||
);
|
||||
|
@@ -1,14 +1,23 @@
|
||||
import { Observable, merge as observableMerge } from 'rxjs';
|
||||
import { filter, first, map, mergeMap, partition, take } from 'rxjs/operators';
|
||||
import { merge as observableMerge, Observable, of as observableOf } from 'rxjs';
|
||||
import {
|
||||
filter,
|
||||
find,
|
||||
first,
|
||||
map,
|
||||
mergeMap,
|
||||
reduce,
|
||||
startWith,
|
||||
switchMap,
|
||||
take,
|
||||
tap
|
||||
} from 'rxjs/operators';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { hasNoValue, hasValue } from '../../shared/empty.util';
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { DSOSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { DSOSuccessResponse, RestResponse } from '../cache/response.models';
|
||||
import { coreSelector, CoreState } from '../core.reducers';
|
||||
import { IndexName } from '../index/index.reducer';
|
||||
import { pathSelector } from '../shared/selectors';
|
||||
@@ -19,13 +28,13 @@ import { GetRequest, RestRequest } from './request.models';
|
||||
import { RequestEntry } from './request.reducer';
|
||||
import { CommitSSBAction } from '../cache/server-sync-buffer.actions';
|
||||
import { RestRequestMethod } from './rest-request-method';
|
||||
import { getResponseFromEntry } from '../shared/operators';
|
||||
|
||||
@Injectable()
|
||||
export class RequestService {
|
||||
private requestsOnTheirWayToTheStore: string[] = [];
|
||||
|
||||
constructor(private objectCache: ObjectCacheService,
|
||||
private responseCache: ResponseCacheService,
|
||||
private uuidService: UUIDService,
|
||||
private store: Store<CoreState>) {
|
||||
}
|
||||
@@ -83,11 +92,15 @@ export class RequestService {
|
||||
|
||||
private isCachedOrPending(request: GetRequest) {
|
||||
let isCached = this.objectCache.hasBySelfLink(request.href);
|
||||
if (!isCached && this.responseCache.has(request.href)) {
|
||||
const responses = this.responseCache.get(request.href).pipe(
|
||||
take(1),
|
||||
map((entry: ResponseCacheEntry) => entry.response)
|
||||
const responses: Observable<RestResponse> = this.isReusable(request.uuid).pipe(
|
||||
filter((reusable: boolean) => !isCached && reusable),
|
||||
switchMap(() => {
|
||||
return this.getByHref(request.href).pipe(
|
||||
getResponseFromEntry(),
|
||||
take(1)
|
||||
);
|
||||
}
|
||||
));
|
||||
|
||||
const errorResponses = responses.pipe(filter((response) => !response.isSuccessful), map(() => true)); // TODO add a configurable number of retries in case of an error.
|
||||
const dsoSuccessResponses = responses.pipe(
|
||||
@@ -99,7 +112,7 @@ export class RequestService {
|
||||
const otherSuccessResponses = responses.pipe(filter((response) => response.isSuccessful && !hasValue((response as DSOSuccessResponse).resourceSelfLinks)), map(() => true));
|
||||
|
||||
observableMerge(errorResponses, otherSuccessResponses, dsoSuccessResponses).subscribe((c) => isCached = c);
|
||||
}
|
||||
|
||||
const isPending = this.isPending(request);
|
||||
return isCached || isPending;
|
||||
}
|
||||
@@ -129,4 +142,34 @@ export class RequestService {
|
||||
commit(method?: RestRequestMethod) {
|
||||
this.store.dispatch(new CommitSSBAction(method))
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a ResponseCacheEntry should still be cached
|
||||
*
|
||||
* @param entry
|
||||
* the entry to check
|
||||
* @return boolean
|
||||
* false if the entry is null, undefined, or its time to
|
||||
* live has been exceeded, true otherwise
|
||||
*/
|
||||
private isReusable(uuid: string): Observable<boolean> {
|
||||
if (hasNoValue(uuid)) {
|
||||
return observableOf(false);
|
||||
} else {
|
||||
const requestEntry$ = this.getByUUID(uuid);
|
||||
return requestEntry$.pipe(
|
||||
filter((entry: RequestEntry) => hasValue(entry) && hasValue(entry.response)),
|
||||
map((entry: RequestEntry) => {
|
||||
if (hasValue(entry) && entry.response.isSuccessful) {
|
||||
const timeOutdated = entry.response.timeAdded + entry.request.responseMsToLive;
|
||||
const isOutDated = new Date().getTime() > timeOutdated;
|
||||
return !isOutDated;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
);
|
||||
return observableOf(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { RestResponse, SearchSuccessResponse } from '../cache/response-cache.models';
|
||||
import { RestResponse, SearchSuccessResponse } from '../cache/response.models';
|
||||
import { DSOResponseParsingService } from './dso-response-parsing.service';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { IntegrationService } from './integration.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
@@ -11,7 +10,6 @@ export class AuthorityService extends IntegrationService {
|
||||
protected browseEndpoint = 'entries';
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected halService: HALEndpointService) {
|
||||
super();
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { ErrorResponse, IntegrationSuccessResponse } from '../cache/response-cache.models';
|
||||
import { ErrorResponse, IntegrationSuccessResponse } from '../cache/response.models';
|
||||
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
|
@@ -6,7 +6,7 @@ import {
|
||||
ErrorResponse,
|
||||
IntegrationSuccessResponse,
|
||||
RestResponse
|
||||
} from '../cache/response-cache.models';
|
||||
} from '../cache/response.models';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { IntegrationObjectFactory } from './integration-object-factory';
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { IntegrationRequest } from '../data/request.models';
|
||||
@@ -18,7 +17,6 @@ class TestService extends IntegrationService {
|
||||
protected browseEndpoint = BROWSE;
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected halService: HALEndpointService) {
|
||||
super();
|
||||
@@ -28,7 +26,6 @@ class TestService extends IntegrationService {
|
||||
describe('IntegrationService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let service: TestService;
|
||||
let responseCache: ResponseCacheService;
|
||||
let requestService: RequestService;
|
||||
let halService: any;
|
||||
let findOptions: IntegrationSearchOptions;
|
||||
@@ -43,24 +40,14 @@ describe('IntegrationService', () => {
|
||||
|
||||
findOptions = new IntegrationSearchOptions(uuid, name, metadata);
|
||||
|
||||
function initMockResponseCacheService(isSuccessful: boolean): ResponseCacheService {
|
||||
return jasmine.createSpyObj('responseCache', {
|
||||
get: cold('c-', {
|
||||
c: {response: {isSuccessful}}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function initTestService(): TestService {
|
||||
return new TestService(
|
||||
responseCache,
|
||||
requestService,
|
||||
halService
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
responseCache = initMockResponseCacheService(true);
|
||||
requestService = getMockRequestService();
|
||||
scheduler = getTestScheduler();
|
||||
halService = new HALEndpointServiceStub(integrationEndpoint);
|
||||
|
@@ -1,26 +1,25 @@
|
||||
import { Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { IntegrationSuccessResponse } from '../cache/response-cache.models';
|
||||
import { IntegrationSuccessResponse } from '../cache/response.models';
|
||||
import { GetRequest, IntegrationRequest } from '../data/request.models';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { IntegrationData } from './integration-data';
|
||||
import { IntegrationSearchOptions } from './models/integration-options.model';
|
||||
import { RequestEntry } from '../data/request.reducer';
|
||||
import { getResponseFromEntry } from '../shared/operators';
|
||||
|
||||
export abstract class IntegrationService {
|
||||
protected request: IntegrationRequest;
|
||||
protected abstract responseCache: ResponseCacheService;
|
||||
protected abstract requestService: RequestService;
|
||||
protected abstract linkPath: string;
|
||||
protected abstract browseEndpoint: string;
|
||||
protected abstract halService: HALEndpointService;
|
||||
|
||||
protected getData(request: GetRequest): Observable<IntegrationData> {
|
||||
return this.responseCache.get(request.href).pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
return this.requestService.getByHref(request.href).pipe(
|
||||
getResponseFromEntry(),
|
||||
mergeMap((response) => {
|
||||
if (response.isSuccessful && isNotEmpty(response)) {
|
||||
const dataResponse = response as IntegrationSuccessResponse;
|
||||
|
@@ -23,7 +23,6 @@ import { ItemDataService } from '../data/item-data.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
@@ -62,7 +61,6 @@ describe('MetadataService', () => {
|
||||
let store: Store<CoreState>;
|
||||
|
||||
let objectCacheService: ObjectCacheService;
|
||||
let responseCacheService: ResponseCacheService;
|
||||
let requestService: RequestService;
|
||||
let uuidService: UUIDService;
|
||||
let remoteDataBuildService: RemoteDataBuildService;
|
||||
@@ -82,10 +80,9 @@ describe('MetadataService', () => {
|
||||
spyOn(store, 'dispatch');
|
||||
|
||||
objectCacheService = new ObjectCacheService(store);
|
||||
responseCacheService = new ResponseCacheService(store);
|
||||
uuidService = new UUIDService();
|
||||
requestService = new RequestService(objectCacheService, responseCacheService, uuidService, store);
|
||||
remoteDataBuildService = new RemoteDataBuildService(objectCacheService, responseCacheService, requestService);
|
||||
requestService = new RequestService(objectCacheService, uuidService, store);
|
||||
remoteDataBuildService = new RemoteDataBuildService(objectCacheService, requestService);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
@@ -108,7 +105,6 @@ describe('MetadataService', () => {
|
||||
],
|
||||
providers: [
|
||||
{ provide: ObjectCacheService, useValue: objectCacheService },
|
||||
{ provide: ResponseCacheService, useValue: responseCacheService },
|
||||
{ provide: RequestService, useValue: requestService },
|
||||
{ provide: RemoteDataBuildService, useValue: remoteDataBuildService },
|
||||
{ provide: GLOBAL_CONFIG, useValue: ENV_CONFIG },
|
||||
|
@@ -1,24 +1,21 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { RegistryService } from './registry.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||
import { Observable, of as observableOf, combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { RequestEntry } from '../data/request.reducer';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { PageInfo } from '../shared/page-info.model';
|
||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||
import { getMockResponseCacheService } from '../../shared/mocks/mock-response-cache.service';
|
||||
|
||||
import {
|
||||
RegistryBitstreamformatsSuccessResponse,
|
||||
RegistryMetadatafieldsSuccessResponse,
|
||||
RegistryMetadataschemasSuccessResponse
|
||||
} from '../cache/response-cache.models';
|
||||
} from '../cache/response.models';
|
||||
import { Component } from '@angular/core';
|
||||
import { RegistryMetadataschemasResponse } from './registry-metadataschemas-response.model';
|
||||
import { RegistryMetadatafieldsResponse } from './registry-metadatafields-response.model';
|
||||
@@ -146,7 +143,6 @@ describe('RegistryService', () => {
|
||||
DummyComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: ResponseCacheService, useValue: getMockResponseCacheService() },
|
||||
{ provide: RequestService, useValue: getMockRequestService() },
|
||||
{ provide: RemoteDataBuildService, useValue: rdbStub },
|
||||
{ provide: HALEndpointService, useValue: halServiceStub },
|
||||
|
@@ -6,29 +6,29 @@ import { PageInfo } from '../shared/page-info.model';
|
||||
import { MetadataSchema } from '../metadata/metadataschema.model';
|
||||
import { MetadataField } from '../metadata/metadatafield.model';
|
||||
import { BitstreamFormat } from './mock-bitstream-format.model';
|
||||
import { flatMap, map, tap } from 'rxjs/operators';
|
||||
import { filter, flatMap, map, tap } from 'rxjs/operators';
|
||||
import { GetRequest, RestRequest } from '../data/request.models';
|
||||
import { GenericConstructor } from '../shared/generic-constructor';
|
||||
import { ResponseParsingService } from '../data/parsing.service';
|
||||
import { RegistryMetadataschemasResponseParsingService } from '../data/registry-metadataschemas-response-parsing.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { RegistryMetadataschemasResponse } from './registry-metadataschemas-response.model';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import {
|
||||
RegistryBitstreamformatsSuccessResponse,
|
||||
RegistryMetadatafieldsSuccessResponse,
|
||||
RegistryMetadataschemasSuccessResponse
|
||||
} from '../cache/response-cache.models';
|
||||
} from '../cache/response.models';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RegistryMetadatafieldsResponseParsingService } from '../data/registry-metadatafields-response-parsing.service';
|
||||
import { RegistryMetadatafieldsResponse } from './registry-metadatafields-response.model';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||
import { RegistryBitstreamformatsResponseParsingService } from '../data/registry-bitstreamformats-response-parsing.service';
|
||||
import { RegistryBitstreamformatsResponse } from './registry-bitstreamformats-response.model';
|
||||
import { RequestEntry } from '../data/request.reducer';
|
||||
import { getResponseFromEntry } from '../shared/operators';
|
||||
|
||||
@Injectable()
|
||||
export class RegistryService {
|
||||
@@ -37,8 +37,7 @@ export class RegistryService {
|
||||
private metadataFieldsPath = 'metadatafields';
|
||||
private bitstreamFormatsPath = 'bitstreamformats';
|
||||
|
||||
constructor(protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
constructor(protected requestService: RequestService,
|
||||
private rdb: RemoteDataBuildService,
|
||||
private halService: HALEndpointService) {
|
||||
|
||||
@@ -51,12 +50,8 @@ export class RegistryService {
|
||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||
);
|
||||
|
||||
const responseCacheObs = requestObs.pipe(
|
||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
||||
);
|
||||
|
||||
const rmrObs: Observable<RegistryMetadataschemasResponse> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const rmrObs: Observable<RegistryMetadataschemasResponse> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: RegistryMetadataschemasSuccessResponse) => response.metadataschemasResponse)
|
||||
);
|
||||
|
||||
@@ -64,8 +59,8 @@ export class RegistryService {
|
||||
map((rmr: RegistryMetadataschemasResponse) => rmr.metadataschemas)
|
||||
);
|
||||
|
||||
const pageInfoObs: Observable<PageInfo> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const pageInfoObs: Observable<PageInfo> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: RegistryMetadataschemasSuccessResponse) => response.pageInfo)
|
||||
);
|
||||
|
||||
@@ -75,7 +70,7 @@ export class RegistryService {
|
||||
})
|
||||
);
|
||||
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs);
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
|
||||
}
|
||||
|
||||
public getMetadataSchemaByName(schemaName: string): Observable<RemoteData<MetadataSchema>> {
|
||||
@@ -90,12 +85,8 @@ export class RegistryService {
|
||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||
);
|
||||
|
||||
const responseCacheObs = requestObs.pipe(
|
||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
||||
);
|
||||
|
||||
const rmrObs: Observable<RegistryMetadataschemasResponse> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const rmrObs: Observable<RegistryMetadataschemasResponse> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: RegistryMetadataschemasSuccessResponse) => response.metadataschemasResponse)
|
||||
);
|
||||
|
||||
@@ -104,7 +95,7 @@ export class RegistryService {
|
||||
map((metadataSchemas: MetadataSchema[]) => metadataSchemas.filter((value) => value.prefix === schemaName)[0])
|
||||
);
|
||||
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, metadataschemaObs);
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, metadataschemaObs);
|
||||
}
|
||||
|
||||
public getMetadataFieldsBySchema(schema: MetadataSchema, pagination: PaginationComponentOptions): Observable<RemoteData<PaginatedList<MetadataField>>> {
|
||||
@@ -114,12 +105,8 @@ export class RegistryService {
|
||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||
);
|
||||
|
||||
const responseCacheObs = requestObs.pipe(
|
||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
||||
);
|
||||
|
||||
const rmrObs: Observable<RegistryMetadatafieldsResponse> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const rmrObs: Observable<RegistryMetadatafieldsResponse> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: RegistryMetadatafieldsSuccessResponse) => response.metadatafieldsResponse)
|
||||
);
|
||||
|
||||
@@ -128,8 +115,9 @@ export class RegistryService {
|
||||
map((metadataFields: MetadataField[]) => metadataFields.filter((field) => field.schema.id === schema.id))
|
||||
);
|
||||
|
||||
const pageInfoObs: Observable<PageInfo> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const pageInfoObs: Observable<PageInfo> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
|
||||
map((response: RegistryMetadatafieldsSuccessResponse) => response.pageInfo)
|
||||
);
|
||||
|
||||
@@ -139,7 +127,7 @@ export class RegistryService {
|
||||
})
|
||||
);
|
||||
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs);
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
|
||||
}
|
||||
|
||||
public getBitstreamFormats(pagination: PaginationComponentOptions): Observable<RemoteData<PaginatedList<BitstreamFormat>>> {
|
||||
@@ -149,12 +137,8 @@ export class RegistryService {
|
||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||
);
|
||||
|
||||
const responseCacheObs = requestObs.pipe(
|
||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
||||
);
|
||||
|
||||
const rbrObs: Observable<RegistryBitstreamformatsResponse> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const rbrObs: Observable<RegistryBitstreamformatsResponse> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: RegistryBitstreamformatsSuccessResponse) => response.bitstreamformatsResponse)
|
||||
);
|
||||
|
||||
@@ -162,8 +146,8 @@ export class RegistryService {
|
||||
map((rbr: RegistryBitstreamformatsResponse) => rbr.bitstreamformats)
|
||||
);
|
||||
|
||||
const pageInfoObs: Observable<PageInfo> = responseCacheObs.pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
const pageInfoObs: Observable<PageInfo> = requestEntryObs.pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: RegistryBitstreamformatsSuccessResponse) => response.pageInfo)
|
||||
);
|
||||
|
||||
@@ -173,7 +157,7 @@ export class RegistryService {
|
||||
})
|
||||
);
|
||||
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs);
|
||||
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
|
||||
}
|
||||
|
||||
private getMetadataSchemasRequestObs(pagination: PaginationComponentOptions): Observable<RestRequest> {
|
||||
|
@@ -1,14 +1,12 @@
|
||||
import { cold, hot } from 'jasmine-marbles';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { HALEndpointService } from './hal-endpoint.service';
|
||||
import { EndpointMapRequest } from '../data/request.models';
|
||||
|
||||
describe('HALEndpointService', () => {
|
||||
let service: HALEndpointService;
|
||||
let responseCache: ResponseCacheService;
|
||||
let requestService: RequestService;
|
||||
let envConfig: GlobalConfig;
|
||||
|
||||
@@ -19,14 +17,6 @@ describe('HALEndpointService', () => {
|
||||
|
||||
describe('getRootEndpointMap', () => {
|
||||
beforeEach(() => {
|
||||
responseCache = jasmine.createSpyObj('responseCache', {
|
||||
get: hot('a-', {
|
||||
a: {
|
||||
response: { endpointMap: endpointMap }
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
requestService = getMockRequestService();
|
||||
|
||||
envConfig = {
|
||||
@@ -34,7 +24,6 @@ describe('HALEndpointService', () => {
|
||||
} as any;
|
||||
|
||||
service = new HALEndpointService(
|
||||
responseCache,
|
||||
requestService,
|
||||
envConfig
|
||||
);
|
||||
@@ -60,12 +49,6 @@ describe('HALEndpointService', () => {
|
||||
envConfig = {
|
||||
rest: { baseUrl: 'https://rest.api/' }
|
||||
} as any;
|
||||
|
||||
service = new HALEndpointService(
|
||||
responseCache,
|
||||
requestService,
|
||||
envConfig
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the endpoint URL for the service\'s linkPath', () => {
|
||||
@@ -89,7 +72,6 @@ describe('HALEndpointService', () => {
|
||||
describe('isEnabledOnRestApi', () => {
|
||||
beforeEach(() => {
|
||||
service = new HALEndpointService(
|
||||
responseCache,
|
||||
requestService,
|
||||
envConfig
|
||||
);
|
||||
|
@@ -1,21 +1,27 @@
|
||||
import {of as observableOf, Observable } from 'rxjs';
|
||||
import {filter, distinctUntilChanged, map, flatMap, startWith, tap } from 'rxjs/operators';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import {
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
flatMap,
|
||||
map,
|
||||
startWith,
|
||||
switchMap,
|
||||
tap
|
||||
} from 'rxjs/operators';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
import { EndpointMap, EndpointMapSuccessResponse } from '../cache/response-cache.models';
|
||||
import { EndpointMapRequest } from '../data/request.models';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||
import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util';
|
||||
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { GLOBAL_CONFIG } from '../../../config';
|
||||
import { EndpointMap, EndpointMapSuccessResponse } from '../cache/response.models';
|
||||
import { getResponseFromEntry } from './operators';
|
||||
|
||||
@Injectable()
|
||||
export class HALEndpointService {
|
||||
|
||||
constructor(private responseCache: ResponseCacheService,
|
||||
private requestService: RequestService,
|
||||
constructor(private requestService: RequestService,
|
||||
@Inject(GLOBAL_CONFIG) private EnvConfig: GlobalConfig) {
|
||||
}
|
||||
|
||||
@@ -29,12 +35,22 @@ export class HALEndpointService {
|
||||
|
||||
private getEndpointMapAt(href): Observable<EndpointMap> {
|
||||
const request = new EndpointMapRequest(this.requestService.generateRequestId(), href);
|
||||
this.requestService.configure(request);
|
||||
return this.responseCache.get(request.href).pipe(
|
||||
map((entry: ResponseCacheEntry) => entry.response),
|
||||
filter((response: EndpointMapSuccessResponse) => isNotEmpty(response)),
|
||||
|
||||
this.requestService.getByUUID(request.uuid).pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: EndpointMapSuccessResponse) => response.endpointMap),
|
||||
distinctUntilChanged(),);
|
||||
distinctUntilChanged()).subscribe((t) => console.log('uuid', t));
|
||||
this.requestService.getByHref(request.href).pipe(
|
||||
getResponseFromEntry(),
|
||||
map((response: EndpointMapSuccessResponse) => response.endpointMap),
|
||||
distinctUntilChanged()).subscribe((t) => console.log('href', t));
|
||||
|
||||
this.requestService.configure(request);
|
||||
return this.requestService.getByHref(request.href).pipe( /*<-- changing this to UUID breaks it */
|
||||
getResponseFromEntry(),
|
||||
map((response: EndpointMapSuccessResponse) => response.endpointMap),
|
||||
distinctUntilChanged());
|
||||
|
||||
}
|
||||
|
||||
public getEndpoint(linkPath: string): Observable<string> {
|
||||
@@ -48,7 +64,7 @@ export class HALEndpointService {
|
||||
let currentPath;
|
||||
const pipeArguments = path
|
||||
.map((subPath: string, index: number) => [
|
||||
flatMap((href: string) => this.getEndpointMapAt(href)),
|
||||
switchMap((href: string) => this.getEndpointMapAt(href)),
|
||||
map((endpointMap: EndpointMap) => {
|
||||
if (hasValue(endpointMap) && hasValue(endpointMap[subPath])) {
|
||||
currentPath = endpointMap[subPath];
|
||||
|
@@ -1,17 +1,15 @@
|
||||
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||
import { getMockResponseCacheService } from '../../shared/mocks/mock-response-cache.service';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { GetRequest, RestRequest } from '../data/request.models';
|
||||
import { GetRequest } from '../data/request.models';
|
||||
import { RequestEntry } from '../data/request.reducer';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import {
|
||||
configureRequest,
|
||||
filterSuccessfulResponses, getRemoteDataPayload,
|
||||
getRequestFromSelflink, getResourceLinksFromResponse,
|
||||
getResponseFromSelflink
|
||||
filterSuccessfulResponses,
|
||||
getRemoteDataPayload,
|
||||
getRequestFromSelflink,
|
||||
getResourceLinksFromResponse,
|
||||
} from './operators';
|
||||
|
||||
describe('Core Module - RxJS Operators', () => {
|
||||
@@ -64,44 +62,6 @@ describe('Core Module - RxJS Operators', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getResponseFromSelflink', () => {
|
||||
let responseCacheService: ResponseCacheService;
|
||||
|
||||
beforeEach(() => {
|
||||
scheduler = getTestScheduler();
|
||||
});
|
||||
|
||||
it('should return the ResponseCacheEntry corresponding to the self link in the source', () => {
|
||||
responseCacheService = getMockResponseCacheService();
|
||||
|
||||
const source = hot('a', { a: testSelfLink });
|
||||
const result = source.pipe(getResponseFromSelflink(responseCacheService));
|
||||
const expected = cold('a', { a: new ResponseCacheEntry()});
|
||||
|
||||
expect(result).toBeObservable(expected)
|
||||
});
|
||||
|
||||
it('should use the responseCacheService to fetch the response by the request\'s link', () => {
|
||||
responseCacheService = getMockResponseCacheService();
|
||||
|
||||
const source = hot('a', { a: testSelfLink });
|
||||
scheduler.schedule(() => source.pipe(getResponseFromSelflink(responseCacheService)).subscribe());
|
||||
scheduler.flush();
|
||||
|
||||
expect(responseCacheService.get).toHaveBeenCalledWith(testSelfLink)
|
||||
});
|
||||
|
||||
it('shouldn\'t return anything if there is no response matching the request\'s link', () => {
|
||||
responseCacheService = getMockResponseCacheService(undefined, cold('a', { a: undefined }));
|
||||
|
||||
const source = hot('a', { a: testSelfLink });
|
||||
const result = source.pipe(getResponseFromSelflink(responseCacheService));
|
||||
const expected = cold('-');
|
||||
|
||||
expect(result).toBeObservable(expected)
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterSuccessfulResponses', () => {
|
||||
it('should only return responses for which isSuccessful === true', () => {
|
||||
const source = hot('abcde', testRCEs);
|
||||
|
@@ -1,9 +1,7 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter, first, flatMap, map, tap } from 'rxjs/operators';
|
||||
import { hasValueOperator, isNotEmpty } from '../../shared/empty.util';
|
||||
import { DSOSuccessResponse } from '../cache/response-cache.models';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { hasValue, hasValueOperator, isNotEmpty } from '../../shared/empty.util';
|
||||
import { DSOSuccessResponse, RestResponse } from '../cache/response.models';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { RestRequest } from '../data/request.models';
|
||||
import { RequestEntry } from '../data/request.reducer';
|
||||
@@ -24,22 +22,25 @@ export const getRequestFromSelflink = (requestService: RequestService) =>
|
||||
hasValueOperator()
|
||||
);
|
||||
|
||||
export const getResponseFromSelflink = (responseCache: ResponseCacheService) =>
|
||||
(source: Observable<string>): Observable<ResponseCacheEntry> =>
|
||||
export const filterSuccessfulResponses = () =>
|
||||
(source: Observable<RequestEntry>): Observable<RestResponse> =>
|
||||
source.pipe(
|
||||
flatMap((href: string) => responseCache.get(href)),
|
||||
hasValueOperator()
|
||||
getResponseFromEntry(),
|
||||
filter((response: RestResponse) => response.isSuccessful === true),
|
||||
);
|
||||
|
||||
export const filterSuccessfulResponses = () =>
|
||||
(source: Observable<ResponseCacheEntry>): Observable<ResponseCacheEntry> =>
|
||||
source.pipe(filter((entry: ResponseCacheEntry) => entry.response.isSuccessful === true));
|
||||
export const getResponseFromEntry = () =>
|
||||
(source: Observable<RequestEntry>): Observable<RestResponse> =>
|
||||
source.pipe(
|
||||
filter((entry: RequestEntry) => hasValue(entry) && hasValue(entry.response)),
|
||||
map((entry: RequestEntry) => entry.response)
|
||||
);
|
||||
|
||||
export const getResourceLinksFromResponse = () =>
|
||||
(source: Observable<ResponseCacheEntry>): Observable<string[]> =>
|
||||
(source: Observable<RequestEntry>): Observable<string[]> =>
|
||||
source.pipe(
|
||||
filterSuccessfulResponses(),
|
||||
map((entry: ResponseCacheEntry) => (entry.response as DSOSuccessResponse).resourceSelfLinks),
|
||||
map((response: DSOSuccessResponse) => response.resourceSelfLinks),
|
||||
);
|
||||
|
||||
export const configureRequest = (requestService: RequestService) =>
|
||||
|
@@ -1,14 +1,13 @@
|
||||
import {of as observableOf, Observable } from 'rxjs';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
||||
import { ResponseCacheEntry } from '../../core/cache/response-cache.reducer';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { RequestEntry } from '../../core/data/request.reducer';
|
||||
import { hasValue } from '../empty.util';
|
||||
|
||||
export function getMockRemoteDataBuildService(toRemoteDataObservable$?: Observable<RemoteData<any>>): RemoteDataBuildService {
|
||||
return {
|
||||
toRemoteDataObservable: (requestEntry$: Observable<RequestEntry>, responseCache$: Observable<ResponseCacheEntry>, payload$: Observable<any>) => {
|
||||
toRemoteDataObservable: (requestEntry$: Observable<RequestEntry>, payload$: Observable<any>) => {
|
||||
|
||||
if (hasValue(toRemoteDataObservable$)) {
|
||||
return toRemoteDataObservable$;
|
||||
|
@@ -1,16 +0,0 @@
|
||||
import {of as observableOf, Observable } from 'rxjs';
|
||||
import { ResponseCacheEntry } from '../../core/cache/response-cache.reducer';
|
||||
import { ResponseCacheService } from '../../core/cache/response-cache.service';
|
||||
|
||||
export function getMockResponseCacheService(
|
||||
add$: Observable<ResponseCacheEntry> = observableOf(new ResponseCacheEntry()),
|
||||
get$: Observable<ResponseCacheEntry> = observableOf(new ResponseCacheEntry()),
|
||||
has: boolean = false
|
||||
): ResponseCacheService {
|
||||
return jasmine.createSpyObj('ResponseCacheService', {
|
||||
add: add$,
|
||||
get: get$,
|
||||
has,
|
||||
});
|
||||
|
||||
}
|
||||
|
@@ -53,6 +53,7 @@ export function startServer(bootstrap: Type<{}> | NgModuleFactory<{}>) {
|
||||
function ngApp(req, res) {
|
||||
|
||||
function onHandleError(parentZoneDelegate, currentZone, targetZone, error) {
|
||||
console.error('Error:', error);
|
||||
console.warn('Error in SSR, serving for direct CSR');
|
||||
res.sendFile('index.csr.html', { root: './src' });
|
||||
}
|
||||
|
Reference in New Issue
Block a user