mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
intermediate commit
This commit is contained in:
@@ -17,6 +17,7 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
animations: [fadeInOut]
|
animations: [fadeInOut]
|
||||||
})
|
})
|
||||||
|
|
||||||
export class TopLevelCommunityListComponent {
|
export class TopLevelCommunityListComponent {
|
||||||
communitiesRDObs: Observable<RemoteData<PaginatedList<Community>>>;
|
communitiesRDObs: Observable<RemoteData<PaginatedList<Community>>>;
|
||||||
config: PaginationComponentOptions;
|
config: PaginationComponentOptions;
|
||||||
|
@@ -8,21 +8,18 @@ import { SearchService } from './search.service';
|
|||||||
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
||||||
import { ActivatedRoute, Router, UrlTree } from '@angular/router';
|
import { ActivatedRoute, Router, UrlTree } from '@angular/router';
|
||||||
import { RequestService } from '../../core/data/request.service';
|
import { RequestService } from '../../core/data/request.service';
|
||||||
import { ResponseCacheService } from '../../core/cache/response-cache.service';
|
|
||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
|
import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
|
||||||
import { RouterStub } from '../../shared/testing/router-stub';
|
import { RouterStub } from '../../shared/testing/router-stub';
|
||||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
||||||
import { Observable, combineLatest as observableCombineLatest } from 'rxjs';
|
import { Observable, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../paginated-search-options.model';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { ResponseCacheEntry } from '../../core/cache/response-cache.reducer';
|
|
||||||
import { RequestEntry } from '../../core/data/request.reducer';
|
import { RequestEntry } from '../../core/data/request.reducer';
|
||||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||||
import { getMockResponseCacheService } from '../../shared/mocks/mock-response-cache.service';
|
|
||||||
import {
|
import {
|
||||||
FacetConfigSuccessResponse,
|
FacetConfigSuccessResponse,
|
||||||
SearchSuccessResponse
|
SearchSuccessResponse
|
||||||
} from '../../core/cache/response-cache.models';
|
} from '../../core/cache/response.models';
|
||||||
import { SearchQueryResponse } from './search-query-response.model';
|
import { SearchQueryResponse } from './search-query-response.model';
|
||||||
import { SearchFilterConfig } from './search-filter-config.model';
|
import { SearchFilterConfig } from './search-filter-config.model';
|
||||||
import { CommunityDataService } from '../../core/data/community-data.service';
|
import { CommunityDataService } from '../../core/data/community-data.service';
|
||||||
@@ -54,7 +51,6 @@ describe('SearchService', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
{ provide: Router, useValue: router },
|
{ provide: Router, useValue: router },
|
||||||
{ provide: ActivatedRoute, useValue: route },
|
{ provide: ActivatedRoute, useValue: route },
|
||||||
{ provide: ResponseCacheService, useValue: getMockResponseCacheService() },
|
|
||||||
{ provide: RequestService, useValue: getMockRequestService() },
|
{ provide: RequestService, useValue: getMockRequestService() },
|
||||||
{ provide: RemoteDataBuildService, useValue: {} },
|
{ provide: RemoteDataBuildService, useValue: {} },
|
||||||
{ provide: HALEndpointService, useValue: {} },
|
{ provide: HALEndpointService, useValue: {} },
|
||||||
@@ -86,9 +82,8 @@ describe('SearchService', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const remoteDataBuildService = {
|
const remoteDataBuildService = {
|
||||||
toRemoteDataObservable: (requestEntryObs: Observable<RequestEntry>, responseCacheObs: Observable<ResponseCacheEntry>, payloadObs: Observable<any>) => {
|
toRemoteDataObservable: (requestEntryObs: Observable<RequestEntry>, payloadObs: Observable<any>) => {
|
||||||
return observableCombineLatest(requestEntryObs,
|
return observableCombineLatest(requestEntryObs, payloadObs).pipe(
|
||||||
responseCacheObs, payloadObs).pipe(
|
|
||||||
map(([req, res, pay]) => {
|
map(([req, res, pay]) => {
|
||||||
return { req, res, pay };
|
return { req, res, pay };
|
||||||
})
|
})
|
||||||
@@ -113,7 +108,6 @@ describe('SearchService', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
{ provide: Router, useValue: router },
|
{ provide: Router, useValue: router },
|
||||||
{ provide: ActivatedRoute, useValue: route },
|
{ provide: ActivatedRoute, useValue: route },
|
||||||
{ provide: ResponseCacheService, useValue: getMockResponseCacheService() },
|
|
||||||
{ provide: RequestService, useValue: getMockRequestService() },
|
{ provide: RequestService, useValue: getMockRequestService() },
|
||||||
{ provide: RemoteDataBuildService, useValue: remoteDataBuildService },
|
{ provide: RemoteDataBuildService, useValue: remoteDataBuildService },
|
||||||
{ provide: HALEndpointService, useValue: halService },
|
{ provide: HALEndpointService, useValue: halService },
|
||||||
@@ -162,10 +156,8 @@ describe('SearchService', () => {
|
|||||||
const searchOptions = new PaginatedSearchOptions({});
|
const searchOptions = new PaginatedSearchOptions({});
|
||||||
const queryResponse = Object.assign(new SearchQueryResponse(), { objects: [] });
|
const queryResponse = Object.assign(new SearchQueryResponse(), { objects: [] });
|
||||||
const response = new SearchSuccessResponse(queryResponse, '200');
|
const response = new SearchSuccessResponse(queryResponse, '200');
|
||||||
const responseEntry = Object.assign(new ResponseCacheEntry(), { response: response });
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
|
spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
|
||||||
(searchService as any).responseCache.get.and.returnValue(observableOf(responseEntry));
|
|
||||||
/* tslint:disable:no-empty */
|
/* tslint:disable:no-empty */
|
||||||
searchService.search(searchOptions).subscribe((t) => {
|
searchService.search(searchOptions).subscribe((t) => {
|
||||||
}); // subscribe to make sure all methods are called
|
}); // subscribe to make sure all methods are called
|
||||||
@@ -192,10 +184,8 @@ describe('SearchService', () => {
|
|||||||
const endPoint = 'http://endpoint.com/test/config';
|
const endPoint = 'http://endpoint.com/test/config';
|
||||||
const filterConfig = [new SearchFilterConfig()];
|
const filterConfig = [new SearchFilterConfig()];
|
||||||
const response = new FacetConfigSuccessResponse(filterConfig, '200');
|
const response = new FacetConfigSuccessResponse(filterConfig, '200');
|
||||||
const responseEntry = Object.assign(new ResponseCacheEntry(), { response: response });
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
|
spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
|
||||||
(searchService as any).responseCache.get.and.returnValue(observableOf(responseEntry));
|
|
||||||
/* tslint:disable:no-empty */
|
/* tslint:disable:no-empty */
|
||||||
searchService.getConfig(null).subscribe((t) => {
|
searchService.getConfig(null).subscribe((t) => {
|
||||||
}); // subscribe to make sure all methods are called
|
}); // subscribe to make sure all methods are called
|
||||||
@@ -224,10 +214,8 @@ describe('SearchService', () => {
|
|||||||
const requestUrl = endPoint + '?scope=' + scope;
|
const requestUrl = endPoint + '?scope=' + scope;
|
||||||
const filterConfig = [new SearchFilterConfig()];
|
const filterConfig = [new SearchFilterConfig()];
|
||||||
const response = new FacetConfigSuccessResponse(filterConfig, '200');
|
const response = new FacetConfigSuccessResponse(filterConfig, '200');
|
||||||
const responseEntry = Object.assign(new ResponseCacheEntry(), { response: response });
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
|
spyOn((searchService as any).halService, 'getEndpoint').and.returnValue(observableOf(endPoint));
|
||||||
(searchService as any).responseCache.get.and.returnValue(observableOf(responseEntry));
|
|
||||||
/* tslint:disable:no-empty */
|
/* tslint:disable:no-empty */
|
||||||
searchService.getConfig(scope).subscribe((t) => {
|
searchService.getConfig(scope).subscribe((t) => {
|
||||||
}); // subscribe to make sure all methods are called
|
}); // 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 { Injectable, OnDestroy } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
ActivatedRoute,
|
ActivatedRoute,
|
||||||
@@ -7,15 +7,13 @@ import {
|
|||||||
Router,
|
Router,
|
||||||
UrlSegmentGroup
|
UrlSegmentGroup
|
||||||
} from '@angular/router';
|
} 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 { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
||||||
import {
|
import {
|
||||||
FacetConfigSuccessResponse,
|
FacetConfigSuccessResponse,
|
||||||
FacetValueSuccessResponse,
|
FacetValueSuccessResponse,
|
||||||
SearchSuccessResponse
|
SearchSuccessResponse
|
||||||
} from '../../core/cache/response-cache.models';
|
} from '../../core/cache/response.models';
|
||||||
import { ResponseCacheEntry } from '../../core/cache/response-cache.reducer';
|
|
||||||
import { ResponseCacheService } from '../../core/cache/response-cache.service';
|
|
||||||
import { PaginatedList } from '../../core/data/paginated-list';
|
import { PaginatedList } from '../../core/data/paginated-list';
|
||||||
import { ResponseParsingService } from '../../core/data/parsing.service';
|
import { ResponseParsingService } from '../../core/data/parsing.service';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
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 { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
||||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
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 { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||||
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { NormalizedSearchResult } from '../normalized-search-result.model';
|
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 { ViewMode } from '../../core/shared/view-mode.model';
|
||||||
import { ResourceType } from '../../core/shared/resource-type';
|
import { ResourceType } from '../../core/shared/resource-type';
|
||||||
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
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
|
* 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,
|
constructor(private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
private rdb: RemoteDataBuildService,
|
private rdb: RemoteDataBuildService,
|
||||||
private halService: HALEndpointService,
|
private halService: HALEndpointService,
|
||||||
@@ -101,13 +103,9 @@ export class SearchService implements OnDestroy {
|
|||||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
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
|
// get search results from response cache
|
||||||
const sqrObs: Observable<SearchQueryResponse> = responseCacheObs.pipe(
|
const sqrObs: Observable<SearchQueryResponse> = requestEntryObs.pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
map((response: SearchSuccessResponse) => response.results)
|
map((response: SearchSuccessResponse) => response.results)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -139,8 +137,8 @@ export class SearchService implements OnDestroy {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const pageInfoObs: Observable<PageInfo> = responseCacheObs.pipe(
|
const pageInfoObs: Observable<PageInfo> = requestEntryObs.pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
map((response: FacetValueSuccessResponse) => response.pageInfo)
|
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))
|
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
|
// get search results from response cache
|
||||||
const facetConfigObs: Observable<SearchFilterConfig[]> = responseCacheObs.pipe(
|
const facetConfigObs: Observable<SearchFilterConfig[]> = requestEntryObs.pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
map((response: FacetConfigSuccessResponse) =>
|
map((response: FacetConfigSuccessResponse) =>
|
||||||
response.results.map((result: any) => Object.assign(new SearchFilterConfig(), result)))
|
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))
|
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
|
// get search results from response cache
|
||||||
const facetValueObs: Observable<FacetValue[]> = responseCacheObs.pipe(
|
const facetValueObs: Observable<FacetValue[]> = requestEntryObs.pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
map((response: FacetValueSuccessResponse) => response.results)
|
map((response: FacetValueSuccessResponse) => response.results)
|
||||||
);
|
);
|
||||||
|
|
||||||
const pageInfoObs: Observable<PageInfo> = responseCacheObs.pipe(
|
const pageInfoObs: Observable<PageInfo> = requestEntryObs.pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
map((response: FacetValueSuccessResponse) => response.pageInfo)
|
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 { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
||||||
import { Inject, Injectable } from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { GLOBAL_CONFIG } from '../../../config';
|
import { GLOBAL_CONFIG } from '../../../config';
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { AuthGetRequest, AuthPostRequest, PostRequest, RestRequest } from '../data/request.models';
|
import { AuthGetRequest, AuthPostRequest, PostRequest, RestRequest } from '../data/request.models';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
import { AuthStatusResponse, ErrorResponse } from '../cache/response.models';
|
||||||
import { AuthStatusResponse, ErrorResponse } from '../cache/response-cache.models';
|
|
||||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||||
|
import { RequestEntry } from '../data/request.reducer';
|
||||||
|
import { getResponseFromEntry } from '../shared/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthRequestService {
|
export class AuthRequestService {
|
||||||
@@ -19,18 +19,17 @@ export class AuthRequestService {
|
|||||||
|
|
||||||
constructor(@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
constructor(@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||||
protected halService: HALEndpointService,
|
protected halService: HALEndpointService,
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService) {
|
protected requestService: RequestService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fetchRequest(request: RestRequest): Observable<any> {
|
protected fetchRequest(request: RestRequest): Observable<any> {
|
||||||
return this.responseCache.get(request.href).pipe(
|
return this.requestService.getByHref(request.href).pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
// TODO to review when https://github.com/DSpace/dspace-angular/issues/217 will be fixed
|
// 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) => {
|
mergeMap((response) => {
|
||||||
if (response.isSuccessful && isNotEmpty(response)) {
|
if (response.isSuccessful && isNotEmpty(response)) {
|
||||||
return observableOf((response as AuthStatusResponse).response);
|
return observableOf((response as AuthStatusResponse).response);
|
||||||
} else if (!response.isSuccessful) {
|
} else if (!response.isSuccessful) {
|
||||||
return observableThrowError(new Error((response as ErrorResponse).errorMessage));
|
return observableThrowError(new Error((response as ErrorResponse).errorMessage));
|
||||||
}
|
}
|
||||||
|
@@ -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 { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
|
@@ -2,7 +2,7 @@ import { Inject, Injectable } from '@angular/core';
|
|||||||
|
|
||||||
import { AuthObjectFactory } from './auth-object-factory';
|
import { AuthObjectFactory } from './auth-object-factory';
|
||||||
import { BaseResponseParsingService } from '../data/base-response-parsing.service';
|
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 { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { GLOBAL_CONFIG } from '../../../config';
|
import { GLOBAL_CONFIG } from '../../../config';
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
|
@@ -2,10 +2,8 @@ import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
|||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { getMockRemoteDataBuildService } from '../../shared/mocks/mock-remote-data-build.service';
|
import { getMockRemoteDataBuildService } from '../../shared/mocks/mock-remote-data-build.service';
|
||||||
import { getMockRequestService } from '../../shared/mocks/mock-request.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 { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
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 { BrowseEndpointRequest, BrowseEntriesRequest, BrowseItemsRequest } from '../data/request.models';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||||
@@ -14,7 +12,6 @@ import { BrowseService } from './browse.service';
|
|||||||
describe('BrowseService', () => {
|
describe('BrowseService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
let service: BrowseService;
|
let service: BrowseService;
|
||||||
let responseCache: ResponseCacheService;
|
|
||||||
let requestService: RequestService;
|
let requestService: RequestService;
|
||||||
let rdbService: RemoteDataBuildService;
|
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() {
|
function initTestService() {
|
||||||
return new BrowseService(
|
return new BrowseService(
|
||||||
responseCache,
|
|
||||||
requestService,
|
requestService,
|
||||||
halService,
|
halService,
|
||||||
rdbService
|
rdbService
|
||||||
@@ -108,7 +93,6 @@ describe('BrowseService', () => {
|
|||||||
describe('getBrowseDefinitions', () => {
|
describe('getBrowseDefinitions', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
responseCache = initMockResponseCacheService(true);
|
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
rdbService = getMockRemoteDataBuildService();
|
rdbService = getMockRemoteDataBuildService();
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
@@ -147,7 +131,6 @@ describe('BrowseService', () => {
|
|||||||
const mockAuthorName = 'Donald Smith';
|
const mockAuthorName = 'Donald Smith';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
responseCache = initMockResponseCacheService(true);
|
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
rdbService = getMockRemoteDataBuildService();
|
rdbService = getMockRemoteDataBuildService();
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
@@ -221,7 +204,6 @@ describe('BrowseService', () => {
|
|||||||
|
|
||||||
describe('if getBrowseDefinitions fires', () => {
|
describe('if getBrowseDefinitions fires', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
responseCache = initMockResponseCacheService(true);
|
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
rdbService = getMockRemoteDataBuildService();
|
rdbService = getMockRemoteDataBuildService();
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
@@ -277,7 +259,6 @@ describe('BrowseService', () => {
|
|||||||
|
|
||||||
describe('if getBrowseDefinitions doesn\'t fire', () => {
|
describe('if getBrowseDefinitions doesn\'t fire', () => {
|
||||||
it('should return undefined', () => {
|
it('should return undefined', () => {
|
||||||
responseCache = initMockResponseCacheService(true);
|
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
rdbService = getMockRemoteDataBuildService();
|
rdbService = getMockRemoteDataBuildService();
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { distinctUntilChanged, map, startWith } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, startWith, tap } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
ensureArrayHasValue,
|
ensureArrayHasValue, hasValue,
|
||||||
hasValueOperator,
|
hasValueOperator,
|
||||||
isEmpty,
|
isEmpty,
|
||||||
isNotEmpty,
|
isNotEmpty,
|
||||||
@@ -11,16 +11,13 @@ import {
|
|||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { SortOptions } from '../cache/models/sort-options.model';
|
import { SortOptions } from '../cache/models/sort-options.model';
|
||||||
import { GenericSuccessResponse } from '../cache/response-cache.models';
|
import { GenericSuccessResponse } from '../cache/response.models';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { PaginatedList } from '../data/paginated-list';
|
import { PaginatedList } from '../data/paginated-list';
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import {
|
import {
|
||||||
BrowseEndpointRequest,
|
BrowseEndpointRequest,
|
||||||
BrowseEntriesRequest,
|
BrowseEntriesRequest,
|
||||||
BrowseItemsRequest,
|
BrowseItemsRequest,
|
||||||
GetRequest,
|
|
||||||
RestRequest
|
RestRequest
|
||||||
} from '../data/request.models';
|
} from '../data/request.models';
|
||||||
import { RequestService } from '../data/request.service';
|
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 { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import {
|
import {
|
||||||
configureRequest,
|
configureRequest,
|
||||||
filterSuccessfulResponses, getBrowseDefinitionLinks,
|
filterSuccessfulResponses,
|
||||||
|
getBrowseDefinitionLinks,
|
||||||
getRemoteDataPayload,
|
getRemoteDataPayload,
|
||||||
getRequestFromSelflink,
|
getRequestFromSelflink
|
||||||
getResponseFromSelflink
|
|
||||||
} from '../shared/operators';
|
} from '../shared/operators';
|
||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
|
import { RequestEntry } from '../data/request.reducer';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BrowseService {
|
export class BrowseService {
|
||||||
@@ -56,7 +54,6 @@ export class BrowseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected halService: HALEndpointService,
|
protected halService: HALEndpointService,
|
||||||
private rdb: RemoteDataBuildService,
|
private rdb: RemoteDataBuildService,
|
||||||
@@ -73,10 +70,8 @@ export class BrowseService {
|
|||||||
|
|
||||||
const href$ = request$.pipe(map((request: RestRequest) => request.href));
|
const href$ = request$.pipe(map((request: RestRequest) => request.href));
|
||||||
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
||||||
const responseCache$ = href$.pipe(getResponseFromSelflink(this.responseCache));
|
const payload$ = requestEntry$.pipe(
|
||||||
const payload$ = responseCache$.pipe(
|
|
||||||
filterSuccessfulResponses(),
|
filterSuccessfulResponses(),
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
|
||||||
map((response: GenericSuccessResponse<BrowseDefinition[]>) => response.payload),
|
map((response: GenericSuccessResponse<BrowseDefinition[]>) => response.payload),
|
||||||
ensureArrayHasValue(),
|
ensureArrayHasValue(),
|
||||||
map((definitions: BrowseDefinition[]) => definitions
|
map((definitions: BrowseDefinition[]) => definitions
|
||||||
@@ -84,7 +79,7 @@ export class BrowseService {
|
|||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$);
|
return this.rdb.toRemoteDataObservable(requestEntry$, payload$);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBrowseEntriesFor(definitionID: string, options: {
|
getBrowseEntriesFor(definitionID: string, options: {
|
||||||
@@ -118,11 +113,9 @@ export class BrowseService {
|
|||||||
const href$ = request$.pipe(map((request: RestRequest) => request.href));
|
const href$ = request$.pipe(map((request: RestRequest) => request.href));
|
||||||
|
|
||||||
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
||||||
const responseCache$ = href$.pipe(getResponseFromSelflink(this.responseCache));
|
|
||||||
|
|
||||||
const payload$ = responseCache$.pipe(
|
const payload$ = requestEntry$.pipe(
|
||||||
filterSuccessfulResponses(),
|
filterSuccessfulResponses(),
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
|
||||||
map((response: GenericSuccessResponse<BrowseEntry[]>) => new PaginatedList(response.pageInfo, response.payload)),
|
map((response: GenericSuccessResponse<BrowseEntry[]>) => new PaginatedList(response.pageInfo, response.payload)),
|
||||||
map((list: PaginatedList<BrowseEntry>) => Object.assign(list, {
|
map((list: PaginatedList<BrowseEntry>) => Object.assign(list, {
|
||||||
page: list.page ? list.page.map((entry: BrowseEntry) => Object.assign(new BrowseEntry(), entry)) : list.page
|
page: list.page ? list.page.map((entry: BrowseEntry) => Object.assign(new BrowseEntry(), entry)) : list.page
|
||||||
@@ -130,7 +123,7 @@ export class BrowseService {
|
|||||||
distinctUntilChanged()
|
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 href$ = request$.pipe(map((request: RestRequest) => request.href));
|
||||||
|
|
||||||
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
||||||
const responseCache$ = href$.pipe(getResponseFromSelflink(this.responseCache));
|
|
||||||
|
|
||||||
const payload$ = responseCache$.pipe(
|
const payload$ = requestEntry$.pipe(
|
||||||
filterSuccessfulResponses(),
|
filterSuccessfulResponses(),
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
|
||||||
map((response: GenericSuccessResponse<Item[]>) => new PaginatedList(response.pageInfo, response.payload)),
|
map((response: GenericSuccessResponse<Item[]>) => new PaginatedList(response.pageInfo, response.payload)),
|
||||||
map((list: PaginatedList<Item>) => Object.assign(list, {
|
map((list: PaginatedList<Item>) => Object.assign(list, {
|
||||||
page: list.page ? list.page.map((item: DSpaceObject) => Object.assign(new Item(), item)) : list.page
|
page: list.page ? list.page.map((item: DSpaceObject) => Object.assign(new Item(), item)) : list.page
|
||||||
@@ -187,7 +178,7 @@ export class BrowseService {
|
|||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$);
|
return this.rdb.toRemoteDataObservable(requestEntry$, payload$);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBrowseURLFor(metadatumKey: string, linkPath: string): Observable<string> {
|
getBrowseURLFor(metadatumKey: string, linkPath: string): Observable<string> {
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
combineLatest as observableCombineLatest,
|
combineLatest as observableCombineLatest,
|
||||||
of as observableOf,
|
|
||||||
Observable,
|
Observable,
|
||||||
|
of as observableOf,
|
||||||
race as observableRace
|
race as observableRace
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import { Injectable } from '@angular/core';
|
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 { hasValue, hasValueOperator, isEmpty, isNotEmpty } from '../../../shared/empty.util';
|
||||||
import { PaginatedList } from '../../data/paginated-list';
|
import { PaginatedList } from '../../data/paginated-list';
|
||||||
import { RemoteData } from '../../data/remote-data';
|
import { RemoteData } from '../../data/remote-data';
|
||||||
@@ -16,22 +16,18 @@ import { RequestService } from '../../data/request.service';
|
|||||||
|
|
||||||
import { NormalizedObject } from '../models/normalized-object.model';
|
import { NormalizedObject } from '../models/normalized-object.model';
|
||||||
import { ObjectCacheService } from '../object-cache.service';
|
import { ObjectCacheService } from '../object-cache.service';
|
||||||
import { DSOSuccessResponse, ErrorResponse } from '../response-cache.models';
|
import { DSOSuccessResponse, ErrorResponse } from '../response.models';
|
||||||
import { ResponseCacheEntry } from '../response-cache.reducer';
|
|
||||||
import { ResponseCacheService } from '../response-cache.service';
|
|
||||||
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
|
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
|
||||||
import { PageInfo } from '../../shared/page-info.model';
|
import { PageInfo } from '../../shared/page-info.model';
|
||||||
import {
|
import {
|
||||||
|
filterSuccessfulResponses,
|
||||||
getRequestFromSelflink,
|
getRequestFromSelflink,
|
||||||
getResourceLinksFromResponse,
|
getResourceLinksFromResponse
|
||||||
getResponseFromSelflink,
|
|
||||||
filterSuccessfulResponses
|
|
||||||
} from '../../shared/operators';
|
} from '../../shared/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RemoteDataBuildService {
|
export class RemoteDataBuildService {
|
||||||
constructor(protected objectCache: ObjectCacheService,
|
constructor(protected objectCache: ObjectCacheService,
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService) {
|
protected requestService: RequestService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,19 +35,16 @@ export class RemoteDataBuildService {
|
|||||||
if (typeof href$ === 'string') {
|
if (typeof href$ === 'string') {
|
||||||
href$ = observableOf(href$);
|
href$ = observableOf(href$);
|
||||||
}
|
}
|
||||||
const requestHref$ = href$.pipe(flatMap((href: string) =>
|
const requestHref$ = href$.pipe(
|
||||||
this.objectCache.getRequestHrefBySelfLink(href)));
|
switchMap((href: string) =>
|
||||||
|
this.objectCache.getRequestHrefBySelfLink(href)),
|
||||||
|
);
|
||||||
|
|
||||||
const requestEntry$ = observableRace(
|
const requestEntry$ = observableRace(
|
||||||
href$.pipe(getRequestFromSelflink(this.requestService)),
|
href$.pipe(getRequestFromSelflink(this.requestService)),
|
||||||
requestHref$.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.
|
// always use self link if that is cached, only if it isn't, get it via the response.
|
||||||
const payload$ =
|
const payload$ =
|
||||||
observableCombineLatest(
|
observableCombineLatest(
|
||||||
@@ -59,7 +52,7 @@ export class RemoteDataBuildService {
|
|||||||
flatMap((href: string) => this.objectCache.getBySelfLink<TNormalized>(href)),
|
flatMap((href: string) => this.objectCache.getBySelfLink<TNormalized>(href)),
|
||||||
startWith(undefined)
|
startWith(undefined)
|
||||||
),
|
),
|
||||||
responseCache$.pipe(
|
requestEntry$.pipe(
|
||||||
getResourceLinksFromResponse(),
|
getResourceLinksFromResponse(),
|
||||||
flatMap((resourceSelfLinks: string[]) => {
|
flatMap((resourceSelfLinks: string[]) => {
|
||||||
if (isNotEmpty(resourceSelfLinks)) {
|
if (isNotEmpty(resourceSelfLinks)) {
|
||||||
@@ -86,21 +79,21 @@ export class RemoteDataBuildService {
|
|||||||
startWith(undefined),
|
startWith(undefined),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
);
|
);
|
||||||
return this.toRemoteDataObservable(requestEntry$, responseCache$, payload$);
|
return this.toRemoteDataObservable(requestEntry$, payload$);
|
||||||
}
|
}
|
||||||
|
|
||||||
toRemoteDataObservable<T>(requestEntry$: Observable<RequestEntry>, responseCache$: Observable<ResponseCacheEntry>, payload$: Observable<T>) {
|
toRemoteDataObservable<T>(requestEntry$: Observable<RequestEntry>, payload$: Observable<T>) {
|
||||||
return observableCombineLatest(requestEntry$, responseCache$.pipe(startWith(undefined)), payload$).pipe(
|
return observableCombineLatest(requestEntry$, requestEntry$.pipe(startWith(undefined)), payload$).pipe(
|
||||||
map(([reqEntry, resEntry, payload]) => {
|
map(([reqEntry, payload]) => {
|
||||||
const requestPending = hasValue(reqEntry.requestPending) ? reqEntry.requestPending : true;
|
const requestPending = hasValue(reqEntry.requestPending) ? reqEntry.requestPending : true;
|
||||||
const responsePending = hasValue(reqEntry.responsePending) ? reqEntry.responsePending : false;
|
const responsePending = hasValue(reqEntry.responsePending) ? reqEntry.responsePending : false;
|
||||||
let isSuccessful: boolean;
|
let isSuccessful: boolean;
|
||||||
let error: RemoteDataError;
|
let error: RemoteDataError;
|
||||||
if (hasValue(resEntry) && hasValue(resEntry.response)) {
|
if (hasValue(reqEntry) && hasValue(reqEntry.response)) {
|
||||||
isSuccessful = resEntry.response.isSuccessful;
|
isSuccessful = reqEntry.response.isSuccessful;
|
||||||
const errorMessage = isSuccessful === false ? (resEntry.response as ErrorResponse).errorMessage : undefined;
|
const errorMessage = isSuccessful === false ? (reqEntry.response as ErrorResponse).errorMessage : undefined;
|
||||||
if (hasValue(errorMessage)) {
|
if (hasValue(errorMessage)) {
|
||||||
error = new RemoteDataError(resEntry.response.statusCode, errorMessage);
|
error = new RemoteDataError(reqEntry.response.statusCode, errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new RemoteData(
|
return new RemoteData(
|
||||||
@@ -120,9 +113,7 @@ export class RemoteDataBuildService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
const requestEntry$ = href$.pipe(getRequestFromSelflink(this.requestService));
|
||||||
const responseCache$ = href$.pipe(getResponseFromSelflink(this.responseCache));
|
const tDomainList$ = requestEntry$.pipe(
|
||||||
|
|
||||||
const tDomainList$ = responseCache$.pipe(
|
|
||||||
getResourceLinksFromResponse(),
|
getResourceLinksFromResponse(),
|
||||||
flatMap((resourceUUIDs: string[]) => {
|
flatMap((resourceUUIDs: string[]) => {
|
||||||
return this.objectCache.getList(resourceUUIDs).pipe(
|
return this.objectCache.getList(resourceUUIDs).pipe(
|
||||||
@@ -135,12 +126,12 @@ export class RemoteDataBuildService {
|
|||||||
startWith([]),
|
startWith([]),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
);
|
);
|
||||||
|
// tDomainList$.subscribe((t) => {console.log('domainlist', t)});
|
||||||
const pageInfo$ = responseCache$.pipe(
|
const pageInfo$ = requestEntry$.pipe(
|
||||||
filterSuccessfulResponses(),
|
filterSuccessfulResponses(),
|
||||||
map((entry: ResponseCacheEntry) => {
|
map((response: DSOSuccessResponse) => {
|
||||||
if (hasValue((entry.response as DSOSuccessResponse).pageInfo)) {
|
if (hasValue((response as DSOSuccessResponse).pageInfo)) {
|
||||||
const resPageInfo = (entry.response as DSOSuccessResponse).pageInfo;
|
const resPageInfo = (response as DSOSuccessResponse).pageInfo;
|
||||||
if (isNotEmpty(resPageInfo) && resPageInfo.currentPage >= 0) {
|
if (isNotEmpty(resPageInfo) && resPageInfo.currentPage >= 0) {
|
||||||
return Object.assign({}, resPageInfo, { currentPage: resPageInfo.currentPage + 1 });
|
return Object.assign({}, resPageInfo, { currentPage: resPageInfo.currentPage + 1 });
|
||||||
} else {
|
} 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 {
|
build<TNormalized, TDomain>(normalized: TNormalized): TDomain {
|
||||||
@@ -204,8 +195,9 @@ export class RemoteDataBuildService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const domainModel = getMapsTo(normalized.constructor);
|
const domainModel = getMapsTo(normalized.constructor);
|
||||||
|
// console.log('domain model', normalized);
|
||||||
|
|
||||||
return Object.assign(new domainModel(), normalized, links);
|
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 */
|
/* tslint:disable:max-classes-per-file */
|
||||||
export class RestResponse {
|
export class RestResponse {
|
||||||
public toCache = true;
|
public timeAdded: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public isSuccessful: boolean,
|
public isSuccessful: boolean,
|
@@ -1,7 +1,6 @@
|
|||||||
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { ConfigService } from './config.service';
|
import { ConfigService } from './config.service';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { ConfigRequest, FindAllOptions } from '../data/request.models';
|
import { ConfigRequest, FindAllOptions } from '../data/request.models';
|
||||||
@@ -16,7 +15,6 @@ class TestService extends ConfigService {
|
|||||||
protected browseEndpoint = BROWSE;
|
protected browseEndpoint = BROWSE;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected halService: HALEndpointService) {
|
protected halService: HALEndpointService) {
|
||||||
super();
|
super();
|
||||||
@@ -26,7 +24,6 @@ class TestService extends ConfigService {
|
|||||||
describe('ConfigService', () => {
|
describe('ConfigService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
let service: TestService;
|
let service: TestService;
|
||||||
let responseCache: ResponseCacheService;
|
|
||||||
let requestService: RequestService;
|
let requestService: RequestService;
|
||||||
let halService: any;
|
let halService: any;
|
||||||
|
|
||||||
@@ -39,17 +36,9 @@ describe('ConfigService', () => {
|
|||||||
const scopedEndpoint = `${serviceEndpoint}/${scopeName}`;
|
const scopedEndpoint = `${serviceEndpoint}/${scopeName}`;
|
||||||
const searchEndpoint = `${serviceEndpoint}/${BROWSE}?uuid=${scopeID}`;
|
const searchEndpoint = `${serviceEndpoint}/${BROWSE}?uuid=${scopeID}`;
|
||||||
|
|
||||||
function initMockResponseCacheService(isSuccessful: boolean): ResponseCacheService {
|
|
||||||
return jasmine.createSpyObj('responseCache', {
|
|
||||||
get: cold('c-', {
|
|
||||||
c: { response: { isSuccessful } }
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initTestService(): TestService {
|
function initTestService(): TestService {
|
||||||
return new TestService(
|
return new TestService(
|
||||||
responseCache,
|
|
||||||
requestService,
|
requestService,
|
||||||
halService
|
halService
|
||||||
);
|
);
|
||||||
@@ -57,7 +46,6 @@ describe('ConfigService', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
scheduler = getTestScheduler();
|
scheduler = getTestScheduler();
|
||||||
responseCache = initMockResponseCacheService(true);
|
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
halService = new HALEndpointServiceStub(configEndpoint);
|
halService = new HALEndpointServiceStub(configEndpoint);
|
||||||
service = initTestService();
|
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 { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
import { ConfigSuccessResponse } from '../cache/response.models';
|
||||||
import { ConfigSuccessResponse } from '../cache/response-cache.models';
|
|
||||||
import { ConfigRequest, FindAllOptions, RestRequest } from '../data/request.models';
|
import { ConfigRequest, FindAllOptions, RestRequest } from '../data/request.models';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
|
||||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { ConfigData } from './config-data';
|
import { ConfigData } from './config-data';
|
||||||
|
import { RequestEntry } from '../data/request.reducer';
|
||||||
|
import { getResponseFromEntry } from '../shared/operators';
|
||||||
|
|
||||||
export abstract class ConfigService {
|
export abstract class ConfigService {
|
||||||
protected request: ConfigRequest;
|
protected request: ConfigRequest;
|
||||||
protected abstract responseCache: ResponseCacheService;
|
|
||||||
protected abstract requestService: RequestService;
|
protected abstract requestService: RequestService;
|
||||||
protected abstract linkPath: string;
|
protected abstract linkPath: string;
|
||||||
protected abstract browseEndpoint: string;
|
protected abstract browseEndpoint: string;
|
||||||
protected abstract halService: HALEndpointService;
|
protected abstract halService: HALEndpointService;
|
||||||
|
|
||||||
protected getConfig(request: RestRequest): Observable<ConfigData> {
|
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(
|
const errorResponses = responses.pipe(
|
||||||
filter((response) => !response.isSuccessful),
|
filter((response) => !response.isSuccessful),
|
||||||
mergeMap(() => observableThrowError(new Error(`Couldn't retrieve the config`)))
|
mergeMap(() => observableThrowError(new Error(`Couldn't retrieve the config`)))
|
||||||
@@ -94,7 +95,6 @@ export abstract class ConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getConfigBySearch(options: FindAllOptions = {}): Observable<ConfigData> {
|
public getConfigBySearch(options: FindAllOptions = {}): Observable<ConfigData> {
|
||||||
console.log(this.halService.getEndpoint(this.linkPath));
|
|
||||||
return this.halService.getEndpoint(this.linkPath).pipe(
|
return this.halService.getEndpoint(this.linkPath).pipe(
|
||||||
map((endpoint: string) => this.getConfigSearchHref(endpoint, options)),
|
map((endpoint: string) => this.getConfigSearchHref(endpoint, options)),
|
||||||
filter((href: string) => isNotEmpty(href)),
|
filter((href: string) => isNotEmpty(href)),
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { ConfigService } from './config.service';
|
import { ConfigService } from './config.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
|
||||||
@@ -11,7 +10,6 @@ export class SubmissionDefinitionsConfigService extends ConfigService {
|
|||||||
protected browseEndpoint = 'search/findByCollection';
|
protected browseEndpoint = 'search/findByCollection';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected halService: HALEndpointService) {
|
protected halService: HALEndpointService) {
|
||||||
super();
|
super();
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { ConfigService } from './config.service';
|
import { ConfigService } from './config.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
|
||||||
@@ -11,7 +10,6 @@ export class SubmissionFormsConfigService extends ConfigService {
|
|||||||
protected browseEndpoint = '';
|
protected browseEndpoint = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected halService: HALEndpointService) {
|
protected halService: HALEndpointService) {
|
||||||
super();
|
super();
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { ConfigService } from './config.service';
|
import { ConfigService } from './config.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
|
|
||||||
@@ -11,7 +10,6 @@ export class SubmissionSectionsConfigService extends ConfigService {
|
|||||||
protected browseEndpoint = '';
|
protected browseEndpoint = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected halService: HALEndpointService) {
|
protected halService: HALEndpointService) {
|
||||||
super();
|
super();
|
||||||
|
@@ -1,13 +1,11 @@
|
|||||||
|
|
||||||
import { ObjectCacheEffects } from './cache/object-cache.effects';
|
import { ObjectCacheEffects } from './cache/object-cache.effects';
|
||||||
import { ResponseCacheEffects } from './cache/response-cache.effects';
|
|
||||||
import { UUIDIndexEffects } from './index/index.effects';
|
import { UUIDIndexEffects } from './index/index.effects';
|
||||||
import { RequestEffects } from './data/request.effects';
|
import { RequestEffects } from './data/request.effects';
|
||||||
import { AuthEffects } from './auth/auth.effects';
|
import { AuthEffects } from './auth/auth.effects';
|
||||||
import { ServerSyncBufferEffects } from './cache/server-sync-buffer.effects';
|
import { ServerSyncBufferEffects } from './cache/server-sync-buffer.effects';
|
||||||
|
|
||||||
export const coreEffects = [
|
export const coreEffects = [
|
||||||
ResponseCacheEffects,
|
|
||||||
RequestEffects,
|
RequestEffects,
|
||||||
ObjectCacheEffects,
|
ObjectCacheEffects,
|
||||||
UUIDIndexEffects,
|
UUIDIndexEffects,
|
||||||
|
@@ -32,7 +32,6 @@ import { ObjectCacheService } from './cache/object-cache.service';
|
|||||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||||
import { RemoteDataBuildService } from './cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from './cache/builders/remote-data-build.service';
|
||||||
import { RequestService } from './data/request.service';
|
import { RequestService } from './data/request.service';
|
||||||
import { ResponseCacheService } from './cache/response-cache.service';
|
|
||||||
import { EndpointMapResponseParsingService } from './data/endpoint-map-response-parsing.service';
|
import { EndpointMapResponseParsingService } from './data/endpoint-map-response-parsing.service';
|
||||||
import { ServerResponseService } from '../shared/services/server-response.service';
|
import { ServerResponseService } from '../shared/services/server-response.service';
|
||||||
import { NativeWindowFactory, NativeWindowService } from '../shared/services/window.service';
|
import { NativeWindowFactory, NativeWindowService } from '../shared/services/window.service';
|
||||||
@@ -102,7 +101,6 @@ const PROVIDERS = [
|
|||||||
RegistryService,
|
RegistryService,
|
||||||
RemoteDataBuildService,
|
RemoteDataBuildService,
|
||||||
RequestService,
|
RequestService,
|
||||||
ResponseCacheService,
|
|
||||||
EndpointMapResponseParsingService,
|
EndpointMapResponseParsingService,
|
||||||
FacetValueResponseParsingService,
|
FacetValueResponseParsingService,
|
||||||
FacetValueMapResponseParsingService,
|
FacetValueMapResponseParsingService,
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { ActionReducerMap, createFeatureSelector } from '@ngrx/store';
|
import { ActionReducerMap, createFeatureSelector } from '@ngrx/store';
|
||||||
|
|
||||||
import { responseCacheReducer, ResponseCacheState } from './cache/response-cache.reducer';
|
|
||||||
import { objectCacheReducer, ObjectCacheState } from './cache/object-cache.reducer';
|
import { objectCacheReducer, ObjectCacheState } from './cache/object-cache.reducer';
|
||||||
import { indexReducer, IndexState } from './index/index.reducer';
|
import { indexReducer, IndexState } from './index/index.reducer';
|
||||||
import { requestReducer, RequestState } from './data/request.reducer';
|
import { requestReducer, RequestState } from './data/request.reducer';
|
||||||
@@ -9,7 +8,6 @@ import { serverSyncBufferReducer, ServerSyncBufferState } from './cache/server-s
|
|||||||
|
|
||||||
export interface CoreState {
|
export interface CoreState {
|
||||||
'cache/object': ObjectCacheState,
|
'cache/object': ObjectCacheState,
|
||||||
'cache/response': ResponseCacheState,
|
|
||||||
'cache/syncbuffer': ServerSyncBufferState,
|
'cache/syncbuffer': ServerSyncBufferState,
|
||||||
'data/request': RequestState,
|
'data/request': RequestState,
|
||||||
'index': IndexState,
|
'index': IndexState,
|
||||||
@@ -18,7 +16,6 @@ export interface CoreState {
|
|||||||
|
|
||||||
export const coreReducers: ActionReducerMap<CoreState> = {
|
export const coreReducers: ActionReducerMap<CoreState> = {
|
||||||
'cache/object': objectCacheReducer,
|
'cache/object': objectCacheReducer,
|
||||||
'cache/response': responseCacheReducer,
|
|
||||||
'cache/syncbuffer': serverSyncBufferReducer,
|
'cache/syncbuffer': serverSyncBufferReducer,
|
||||||
'data/request': requestReducer,
|
'data/request': requestReducer,
|
||||||
'index': indexReducer,
|
'index': indexReducer,
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { getMockObjectCacheService } from '../../shared/mocks/mock-object-cache.service';
|
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 { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { BrowseEntriesResponseParsingService } from './browse-entries-response-parsing.service';
|
import { BrowseEntriesResponseParsingService } from './browse-entries-response-parsing.service';
|
||||||
import { BrowseEntriesRequest } from './request.models';
|
import { BrowseEntriesRequest } from './request.models';
|
||||||
|
@@ -7,7 +7,7 @@ import {
|
|||||||
ErrorResponse,
|
ErrorResponse,
|
||||||
GenericSuccessResponse,
|
GenericSuccessResponse,
|
||||||
RestResponse
|
RestResponse
|
||||||
} from '../cache/response-cache.models';
|
} from '../cache/response.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||||
import { BrowseEntry } from '../shared/browse-entry.model';
|
import { BrowseEntry } from '../shared/browse-entry.model';
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { getMockObjectCacheService } from '../../shared/mocks/mock-object-cache.service';
|
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 { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { BrowseEntriesResponseParsingService } from './browse-entries-response-parsing.service';
|
import { BrowseEntriesResponseParsingService } from './browse-entries-response-parsing.service';
|
||||||
import { BrowseEntriesRequest, BrowseItemsRequest } from './request.models';
|
import { BrowseEntriesRequest, BrowseItemsRequest } from './request.models';
|
||||||
|
@@ -7,7 +7,7 @@ import {
|
|||||||
ErrorResponse,
|
ErrorResponse,
|
||||||
GenericSuccessResponse,
|
GenericSuccessResponse,
|
||||||
RestResponse
|
RestResponse
|
||||||
} from '../cache/response-cache.models';
|
} from '../cache/response.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||||
import { BaseResponseParsingService } from './base-response-parsing.service';
|
import { BaseResponseParsingService } from './base-response-parsing.service';
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { BrowseResponseParsingService } from './browse-response-parsing.service';
|
import { BrowseResponseParsingService } from './browse-response-parsing.service';
|
||||||
import { BrowseEndpointRequest } from './request.models';
|
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 { BrowseDefinition } from '../shared/browse-definition.model';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.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 { ResponseParsingService } from './parsing.service';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
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 { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||||
|
@@ -1,10 +1,8 @@
|
|||||||
import { Inject, Injectable } from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { NormalizedCollection } from '../cache/models/normalized-collection.model';
|
import { NormalizedCollection } from '../cache/models/normalized-collection.model';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { Collection } from '../shared/collection.model';
|
import { Collection } from '../shared/collection.model';
|
||||||
import { ComColDataService } from './comcol-data.service';
|
import { ComColDataService } from './comcol-data.service';
|
||||||
@@ -17,7 +15,6 @@ export class CollectionDataService extends ComColDataService<NormalizedCollectio
|
|||||||
protected linkPath = 'collections';
|
protected linkPath = 'collections';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected rdbService: RemoteDataBuildService,
|
protected rdbService: RemoteDataBuildService,
|
||||||
protected store: Store<CoreState>,
|
protected store: Store<CoreState>,
|
||||||
|
@@ -5,7 +5,6 @@ import { GlobalConfig } from '../../../config';
|
|||||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { ComColDataService } from './comcol-data.service';
|
import { ComColDataService } from './comcol-data.service';
|
||||||
import { CommunityDataService } from './community-data.service';
|
import { CommunityDataService } from './community-data.service';
|
||||||
@@ -23,7 +22,6 @@ class NormalizedTestObject extends NormalizedObject {
|
|||||||
class TestService extends ComColDataService<NormalizedTestObject, any> {
|
class TestService extends ComColDataService<NormalizedTestObject, any> {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected rdbService: RemoteDataBuildService,
|
protected rdbService: RemoteDataBuildService,
|
||||||
protected store: Store<CoreState>,
|
protected store: Store<CoreState>,
|
||||||
@@ -41,7 +39,6 @@ class TestService extends ComColDataService<NormalizedTestObject, any> {
|
|||||||
describe('ComColDataService', () => {
|
describe('ComColDataService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
let service: TestService;
|
let service: TestService;
|
||||||
let responseCache: ResponseCacheService;
|
|
||||||
let requestService: RequestService;
|
let requestService: RequestService;
|
||||||
let cds: CommunityDataService;
|
let cds: CommunityDataService;
|
||||||
let objectCache: ObjectCacheService;
|
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 {
|
function initMockObjectCacheService(): ObjectCacheService {
|
||||||
return jasmine.createSpyObj('objectCache', {
|
return jasmine.createSpyObj('objectCache', {
|
||||||
getByUUID: cold('d-', {
|
getByUUID: cold('d-', {
|
||||||
@@ -90,7 +79,6 @@ describe('ComColDataService', () => {
|
|||||||
|
|
||||||
function initTestService(): TestService {
|
function initTestService(): TestService {
|
||||||
return new TestService(
|
return new TestService(
|
||||||
responseCache,
|
|
||||||
requestService,
|
requestService,
|
||||||
rdbService,
|
rdbService,
|
||||||
store,
|
store,
|
||||||
@@ -111,7 +99,6 @@ describe('ComColDataService', () => {
|
|||||||
cds = initMockCommunityDataService();
|
cds = initMockCommunityDataService();
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
objectCache = initMockObjectCacheService();
|
objectCache = initMockObjectCacheService();
|
||||||
responseCache = initMockResponseCacheService(true);
|
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
|
|
||||||
const expected = new FindByIDRequest(requestService.generateRequestId(), communityEndpoint, scopeID);
|
const expected = new FindByIDRequest(requestService.generateRequestId(), communityEndpoint, scopeID);
|
||||||
@@ -127,7 +114,6 @@ describe('ComColDataService', () => {
|
|||||||
cds = initMockCommunityDataService();
|
cds = initMockCommunityDataService();
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
objectCache = initMockObjectCacheService();
|
objectCache = initMockObjectCacheService();
|
||||||
responseCache = initMockResponseCacheService(true);
|
|
||||||
service = initTestService();
|
service = initTestService();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -150,7 +136,6 @@ describe('ComColDataService', () => {
|
|||||||
cds = initMockCommunityDataService();
|
cds = initMockCommunityDataService();
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
objectCache = initMockObjectCacheService();
|
objectCache = initMockObjectCacheService();
|
||||||
responseCache = initMockResponseCacheService(false);
|
|
||||||
service = initTestService();
|
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 { 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 { NormalizedCommunity } from '../cache/models/normalized-community.model';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
|
||||||
import { CommunityDataService } from './community-data.service';
|
import { CommunityDataService } from './community-data.service';
|
||||||
|
|
||||||
import { DataService } from './data.service';
|
import { DataService } from './data.service';
|
||||||
import { FindAllOptions, FindByIDRequest } from './request.models';
|
import { FindAllOptions, FindByIDRequest } from './request.models';
|
||||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
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> {
|
export abstract class ComColDataService<TNormalized extends NormalizedObject, TDomain> extends DataService<TNormalized, TDomain> {
|
||||||
protected abstract cds: CommunityDataService;
|
protected abstract cds: CommunityDataService;
|
||||||
@@ -26,9 +27,9 @@ export abstract class ComColDataService<TNormalized extends NormalizedObject, TD
|
|||||||
* @return { Observable<string> }
|
* @return { Observable<string> }
|
||||||
* an Observable<string> containing the scoped URL
|
* 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)) {
|
if (isEmpty(options.scopeID)) {
|
||||||
return this.halService.getEndpoint(this.linkPath);
|
return this.halService.getEndpoint(linkPath);
|
||||||
} else {
|
} else {
|
||||||
const scopeCommunityHrefObs = this.cds.getEndpoint().pipe(
|
const scopeCommunityHrefObs = this.cds.getEndpoint().pipe(
|
||||||
mergeMap((endpoint: string) => this.cds.getFindByIDHref(endpoint, options.scopeID)),
|
mergeMap((endpoint: string) => this.cds.getFindByIDHref(endpoint, options.scopeID)),
|
||||||
@@ -37,7 +38,7 @@ export abstract class ComColDataService<TNormalized extends NormalizedObject, TD
|
|||||||
tap((href: string) => {
|
tap((href: string) => {
|
||||||
const request = new FindByIDRequest(this.requestService.generateRequestId(), href, options.scopeID);
|
const request = new FindByIDRequest(this.requestService.generateRequestId(), href, options.scopeID);
|
||||||
this.requestService.configure(request);
|
this.requestService.configure(request);
|
||||||
}),);
|
}));
|
||||||
|
|
||||||
// return scopeCommunityHrefObs.pipe(
|
// return scopeCommunityHrefObs.pipe(
|
||||||
// mergeMap((href: string) => this.responseCache.get(href)),
|
// mergeMap((href: string) => this.responseCache.get(href)),
|
||||||
@@ -46,7 +47,7 @@ export abstract class ComColDataService<TNormalized extends NormalizedObject, TD
|
|||||||
// if (response.isSuccessful) {
|
// if (response.isSuccessful) {
|
||||||
// const community$: Observable<NormalizedCommunity> = this.objectCache.getByUUID(scopeID);
|
// const community$: Observable<NormalizedCommunity> = this.objectCache.getByUUID(scopeID);
|
||||||
// return community$.pipe(
|
// return community$.pipe(
|
||||||
// map((community) => community._links[this.linkPath]),
|
// map((community) => community._links[linkPath]),
|
||||||
// filter((href) => isNotEmpty(href)),
|
// filter((href) => isNotEmpty(href)),
|
||||||
// distinctUntilChanged()
|
// distinctUntilChanged()
|
||||||
// );
|
// );
|
||||||
@@ -57,8 +58,8 @@ export abstract class ComColDataService<TNormalized extends NormalizedObject, TD
|
|||||||
// distinctUntilChanged()
|
// distinctUntilChanged()
|
||||||
// );
|
// );
|
||||||
const responses = scopeCommunityHrefObs.pipe(
|
const responses = scopeCommunityHrefObs.pipe(
|
||||||
mergeMap((href: string) => this.responseCache.get(href)),
|
mergeMap((href: string) => this.requestService.getByHref(href)),
|
||||||
map((entry: ResponseCacheEntry) => entry.response));
|
getResponseFromEntry());
|
||||||
const errorResponses = responses.pipe(
|
const errorResponses = responses.pipe(
|
||||||
filter((response) => !response.isSuccessful),
|
filter((response) => !response.isSuccessful),
|
||||||
mergeMap(() => observableThrowError(new Error(`The Community with scope ${options.scopeID} couldn't be retrieved`)))
|
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(
|
const successResponses = responses.pipe(
|
||||||
filter((response) => response.isSuccessful),
|
filter((response) => response.isSuccessful),
|
||||||
mergeMap(() => this.objectCache.getByUUID(options.scopeID)),
|
mergeMap(() => this.objectCache.getByUUID(options.scopeID)),
|
||||||
map((nc: NormalizedCommunity) => nc._links[this.linkPath]),
|
map((nc: NormalizedCommunity) => nc._links[linkPath]),
|
||||||
filter((href) => isNotEmpty(href))
|
filter((href) => isNotEmpty(href))
|
||||||
);
|
);
|
||||||
|
|
||||||
return observableMerge(errorResponses, successResponses).pipe(distinctUntilChanged());
|
return observableMerge(errorResponses, successResponses).pipe(distinctUntilChanged(), share());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,10 @@
|
|||||||
|
import { filter, mergeMap, take } from 'rxjs/operators';
|
||||||
import {mergeMap, filter, take} from 'rxjs/operators';
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { NormalizedCommunity } from '../cache/models/normalized-community.model';
|
import { NormalizedCommunity } from '../cache/models/normalized-community.model';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { Community } from '../shared/community.model';
|
import { Community } from '../shared/community.model';
|
||||||
import { ComColDataService } from './comcol-data.service';
|
import { ComColDataService } from './comcol-data.service';
|
||||||
@@ -25,7 +23,6 @@ export class CommunityDataService extends ComColDataService<NormalizedCommunity,
|
|||||||
protected cds = this;
|
protected cds = this;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected rdbService: RemoteDataBuildService,
|
protected rdbService: RemoteDataBuildService,
|
||||||
protected store: Store<CoreState>,
|
protected store: Store<CoreState>,
|
||||||
@@ -40,12 +37,10 @@ export class CommunityDataService extends ComColDataService<NormalizedCommunity,
|
|||||||
}
|
}
|
||||||
|
|
||||||
findTop(options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<Community>>> {
|
findTop(options: FindAllOptions = {}): Observable<RemoteData<PaginatedList<Community>>> {
|
||||||
const hrefObs = this.halService.getEndpoint(this.topLinkPath).pipe(filter((href: string) => isNotEmpty(href)),
|
const hrefObs = this.getFindAllHref(options, this.topLinkPath);
|
||||||
mergeMap((endpoint: string) => this.getFindAllHref(options)),);
|
|
||||||
|
|
||||||
hrefObs.pipe(
|
hrefObs.pipe(
|
||||||
filter((href: string) => hasValue(href)),
|
filter((href: string) => hasValue(href)),
|
||||||
take(1),)
|
take(1))
|
||||||
.subscribe((href: string) => {
|
.subscribe((href: string) => {
|
||||||
const request = new FindAllRequest(this.requestService.generateRequestId(), href, options);
|
const request = new FindAllRequest(this.requestService.generateRequestId(), href, options);
|
||||||
this.requestService.configure(request);
|
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 { ConfigResponseParsingService } from './config-response-parsing.service';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
|
@@ -3,7 +3,7 @@ import { Inject, Injectable } from '@angular/core';
|
|||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from './parsing.service';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
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 { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { ConfigObjectFactory } from '../shared/config/config-object-factory';
|
import { ConfigObjectFactory } from '../shared/config/config-object-factory';
|
||||||
|
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { DataService } from './data.service';
|
import { DataService } from './data.service';
|
||||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
@@ -22,7 +21,6 @@ class NormalizedTestObject extends NormalizedObject {
|
|||||||
|
|
||||||
class TestService extends DataService<NormalizedTestObject, any> {
|
class TestService extends DataService<NormalizedTestObject, any> {
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected rdbService: RemoteDataBuildService,
|
protected rdbService: RemoteDataBuildService,
|
||||||
protected store: Store<CoreState>,
|
protected store: Store<CoreState>,
|
||||||
@@ -33,7 +31,7 @@ class TestService extends DataService<NormalizedTestObject, any> {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getBrowseEndpoint(options: FindAllOptions): Observable<string> {
|
public getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
|
||||||
return observableOf(endpoint);
|
return observableOf(endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +39,6 @@ class TestService extends DataService<NormalizedTestObject, any> {
|
|||||||
describe('DataService', () => {
|
describe('DataService', () => {
|
||||||
let service: TestService;
|
let service: TestService;
|
||||||
let options: FindAllOptions;
|
let options: FindAllOptions;
|
||||||
const responseCache = {} as ResponseCacheService;
|
|
||||||
const requestService = {} as RequestService;
|
const requestService = {} as RequestService;
|
||||||
const halService = {} as HALEndpointService;
|
const halService = {} as HALEndpointService;
|
||||||
const rdbService = {} as RemoteDataBuildService;
|
const rdbService = {} as RemoteDataBuildService;
|
||||||
@@ -57,7 +54,6 @@ describe('DataService', () => {
|
|||||||
|
|
||||||
function initTestService(): TestService {
|
function initTestService(): TestService {
|
||||||
return new TestService(
|
return new TestService(
|
||||||
responseCache,
|
|
||||||
requestService,
|
requestService,
|
||||||
rdbService,
|
rdbService,
|
||||||
store,
|
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 { Observable } from 'rxjs';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
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 { compare, Operation } from 'fast-json-patch';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
|
|
||||||
export abstract class DataService<TNormalized extends NormalizedObject, TDomain> {
|
export abstract class DataService<TNormalized extends NormalizedObject, TDomain> {
|
||||||
protected abstract responseCache: ResponseCacheService;
|
|
||||||
protected abstract requestService: RequestService;
|
protected abstract requestService: RequestService;
|
||||||
protected abstract rdbService: RemoteDataBuildService;
|
protected abstract rdbService: RemoteDataBuildService;
|
||||||
protected abstract store: Store<CoreState>;
|
protected abstract store: Store<CoreState>;
|
||||||
@@ -25,34 +24,31 @@ export abstract class DataService<TNormalized extends NormalizedObject, TDomain>
|
|||||||
protected abstract halService: HALEndpointService;
|
protected abstract halService: HALEndpointService;
|
||||||
protected abstract objectCache: ObjectCacheService;
|
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>;
|
let result: Observable<string>;
|
||||||
const args = [];
|
const args = [];
|
||||||
|
|
||||||
result = this.getBrowseEndpoint(options).pipe(distinctUntilChanged());
|
result = this.getBrowseEndpoint(options, linkPath);
|
||||||
|
|
||||||
if (hasValue(options.currentPage) && typeof options.currentPage === 'number') {
|
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 */
|
/* 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}`);
|
args.push(`page=${options.currentPage - 1}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasValue(options.elementsPerPage)) {
|
if (hasValue(options.elementsPerPage)) {
|
||||||
args.push(`size=${options.elementsPerPage}`);
|
args.push(`size=${options.elementsPerPage}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasValue(options.sort)) {
|
if (hasValue(options.sort)) {
|
||||||
args.push(`sort=${options.sort.field},${options.sort.direction}`);
|
args.push(`sort=${options.sort.field},${options.sort.direction}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasValue(options.startsWith)) {
|
if (hasValue(options.startsWith)) {
|
||||||
args.push(`startsWith=${options.startsWith}`);
|
args.push(`startsWith=${options.startsWith}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNotEmpty(args)) {
|
if (isNotEmpty(args)) {
|
||||||
return result.pipe(map((href: string) => new URLCombiner(href, `?${args.join('&')}`).toString()));
|
return result.pipe(map((href: string) => new URLCombiner(href, `?${args.join('&')}`).toString()));
|
||||||
} else {
|
} else {
|
||||||
|
result.subscribe((t) => console.log(t));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,6 +111,7 @@ export abstract class DataService<TNormalized extends NormalizedObject, TDomain>
|
|||||||
this.objectCache.addPatch(object.self, operations);
|
this.objectCache.addPatch(object.self, operations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO implement, after the structure of the REST server's POST response is finalized
|
// TODO implement, after the structure of the REST server's POST response is finalized
|
||||||
// create(dso: DSpaceObject): Observable<RemoteData<TDomain>> {
|
// create(dso: DSpaceObject): Observable<RemoteData<TDomain>> {
|
||||||
// const postHrefObs = this.getEndpoint();
|
// const postHrefObs = this.getEndpoint();
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
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 { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from './parsing.service';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
|
@@ -7,7 +7,7 @@ import { NormalizedObject } from '../cache/models/normalized-object.model';
|
|||||||
import { ResourceType } from '../shared/resource-type';
|
import { ResourceType } from '../shared/resource-type';
|
||||||
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
|
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
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 { RestRequest } from './request.models';
|
||||||
|
|
||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from './parsing.service';
|
||||||
@@ -23,12 +23,14 @@ export class DSOResponseParsingService extends BaseResponseParsingService implem
|
|||||||
constructor(
|
constructor(
|
||||||
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||||
protected objectCache: ObjectCacheService,
|
protected objectCache: ObjectCacheService,
|
||||||
) { super();
|
) {
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
||||||
const processRequestDTO = this.process<NormalizedObject,ResourceType>(data.payload, request.href);
|
const processRequestDTO = this.process<NormalizedObject, ResourceType>(data.payload, request.href);
|
||||||
let objectList = processRequestDTO;
|
let objectList = processRequestDTO;
|
||||||
|
|
||||||
if (hasNoValue(processRequestDTO)) {
|
if (hasNoValue(processRequestDTO)) {
|
||||||
return new DSOSuccessResponse([], data.statusCode, undefined)
|
return new DSOSuccessResponse([], data.statusCode, undefined)
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@ import { Store } from '@ngrx/store';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { NormalizedDSpaceObject } from '../cache/models/normalized-dspace-object.model';
|
import { NormalizedDSpaceObject } from '../cache/models/normalized-dspace-object.model';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
@@ -18,7 +17,6 @@ class DataServiceImpl extends DataService<NormalizedDSpaceObject, DSpaceObject>
|
|||||||
protected linkPath = 'dso';
|
protected linkPath = 'dso';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected rdbService: RemoteDataBuildService,
|
protected rdbService: RemoteDataBuildService,
|
||||||
protected store: Store<CoreState>,
|
protected store: Store<CoreState>,
|
||||||
@@ -27,8 +25,8 @@ class DataServiceImpl extends DataService<NormalizedDSpaceObject, DSpaceObject>
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
getBrowseEndpoint(options: FindAllOptions): Observable<string> {
|
getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
|
||||||
return this.halService.getEndpoint(this.linkPath);
|
return this.halService.getEndpoint(linkPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFindByIDHref(endpoint, resourceID): string {
|
getFindByIDHref(endpoint, resourceID): string {
|
||||||
@@ -46,7 +44,7 @@ export class DSpaceObjectDataService {
|
|||||||
protected rdbService: RemoteDataBuildService,
|
protected rdbService: RemoteDataBuildService,
|
||||||
protected halService: HALEndpointService,
|
protected halService: HALEndpointService,
|
||||||
protected objectCache: ObjectCacheService) {
|
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>> {
|
findById(uuid: string): Observable<RemoteData<DSpaceObject>> {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { GLOBAL_CONFIG } from '../../../config';
|
import { GLOBAL_CONFIG } from '../../../config';
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
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 { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from './parsing.service';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
|
@@ -2,7 +2,7 @@ import { Inject, Injectable } from '@angular/core';
|
|||||||
import {
|
import {
|
||||||
FacetConfigSuccessResponse,
|
FacetConfigSuccessResponse,
|
||||||
RestResponse
|
RestResponse
|
||||||
} from '../cache/response-cache.models';
|
} from '../cache/response.models';
|
||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from './parsing.service';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
|
@@ -4,7 +4,7 @@ import {
|
|||||||
FacetValueMapSuccessResponse,
|
FacetValueMapSuccessResponse,
|
||||||
FacetValueSuccessResponse,
|
FacetValueSuccessResponse,
|
||||||
RestResponse
|
RestResponse
|
||||||
} from '../cache/response-cache.models';
|
} from '../cache/response.models';
|
||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from './parsing.service';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
|
@@ -4,7 +4,7 @@ import {
|
|||||||
FacetValueMapSuccessResponse,
|
FacetValueMapSuccessResponse,
|
||||||
FacetValueSuccessResponse,
|
FacetValueSuccessResponse,
|
||||||
RestResponse
|
RestResponse
|
||||||
} from '../cache/response-cache.models';
|
} from '../cache/response.models';
|
||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from './parsing.service';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
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 { TestScheduler } from 'rxjs/testing';
|
||||||
import { BrowseService } from '../browse/browse.service';
|
import { BrowseService } from '../browse/browse.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { ItemDataService } from './item-data.service';
|
import { ItemDataService } from './item-data.service';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
@@ -16,7 +15,6 @@ describe('ItemDataService', () => {
|
|||||||
let service: ItemDataService;
|
let service: ItemDataService;
|
||||||
let bs: BrowseService;
|
let bs: BrowseService;
|
||||||
const requestService = {} as RequestService;
|
const requestService = {} as RequestService;
|
||||||
const responseCache = {} as ResponseCacheService;
|
|
||||||
const rdbService = {} as RemoteDataBuildService;
|
const rdbService = {} as RemoteDataBuildService;
|
||||||
const objectCache = {} as ObjectCacheService;
|
const objectCache = {} as ObjectCacheService;
|
||||||
const store = {} as Store<CoreState>;
|
const store = {} as Store<CoreState>;
|
||||||
@@ -48,7 +46,6 @@ describe('ItemDataService', () => {
|
|||||||
|
|
||||||
function initTestService() {
|
function initTestService() {
|
||||||
return new ItemDataService(
|
return new ItemDataService(
|
||||||
responseCache,
|
|
||||||
requestService,
|
requestService,
|
||||||
rdbService,
|
rdbService,
|
||||||
store,
|
store,
|
||||||
|
@@ -7,7 +7,6 @@ import { isNotEmpty } from '../../shared/empty.util';
|
|||||||
import { BrowseService } from '../browse/browse.service';
|
import { BrowseService } from '../browse/browse.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { NormalizedItem } from '../cache/models/normalized-item.model';
|
import { NormalizedItem } from '../cache/models/normalized-item.model';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { Item } from '../shared/item.model';
|
import { Item } from '../shared/item.model';
|
||||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||||
@@ -23,7 +22,6 @@ export class ItemDataService extends DataService<NormalizedItem, Item> {
|
|||||||
protected linkPath = 'items';
|
protected linkPath = 'items';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected rdbService: RemoteDataBuildService,
|
protected rdbService: RemoteDataBuildService,
|
||||||
protected store: Store<CoreState>,
|
protected store: Store<CoreState>,
|
||||||
@@ -39,12 +37,12 @@ export class ItemDataService extends DataService<NormalizedItem, Item> {
|
|||||||
* @param {FindAllOptions} options
|
* @param {FindAllOptions} options
|
||||||
* @returns {Observable<string>}
|
* @returns {Observable<string>}
|
||||||
*/
|
*/
|
||||||
public getBrowseEndpoint(options: FindAllOptions = {}): Observable<string> {
|
public getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
|
||||||
let field = 'dc.date.issued';
|
let field = 'dc.date.issued';
|
||||||
if (options.sort && options.sort.field) {
|
if (options.sort && options.sort.field) {
|
||||||
field = 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)),
|
filter((href: string) => isNotEmpty(href)),
|
||||||
map((href: string) => new URLCombiner(href, `?scope=${options.scopeID}`).toString()),
|
map((href: string) => new URLCombiner(href, `?scope=${options.scopeID}`).toString()),
|
||||||
distinctUntilChanged(),);
|
distinctUntilChanged(),);
|
||||||
|
@@ -4,7 +4,7 @@ import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.
|
|||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from './parsing.service';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { MetadataschemaSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
import { MetadataschemaSuccessResponse, RestResponse } from '../cache/response.models';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MetadataschemaParsingService implements ResponseParsingService {
|
export class MetadataschemaParsingService implements ResponseParsingService {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { RestResponse } from '../cache/response-cache.models';
|
import { RestResponse } from '../cache/response.models';
|
||||||
|
|
||||||
export interface ResponseParsingService {
|
export interface ResponseParsingService {
|
||||||
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse;
|
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 { RegistryBitstreamformatsResponse } from '../registry/registry-bitstreamformats-response.model';
|
||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
RegistryMetadatafieldsSuccessResponse,
|
RegistryMetadatafieldsSuccessResponse,
|
||||||
RestResponse
|
RestResponse
|
||||||
} from '../cache/response-cache.models';
|
} from '../cache/response.models';
|
||||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { ResponseParsingService } from './parsing.service';
|
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 { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from './parsing.service';
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Action } from '@ngrx/store';
|
import { Action } from '@ngrx/store';
|
||||||
import { type } from '../../shared/ngrx/type';
|
import { type } from '../../shared/ngrx/type';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
|
import { RestResponse } from '../cache/response.models';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of RequestAction type definitions
|
* The list of RequestAction type definitions
|
||||||
@@ -8,7 +9,8 @@ import { RestRequest } from './request.models';
|
|||||||
export const RequestActionTypes = {
|
export const RequestActionTypes = {
|
||||||
CONFIGURE: type('dspace/core/data/request/CONFIGURE'),
|
CONFIGURE: type('dspace/core/data/request/CONFIGURE'),
|
||||||
EXECUTE: type('dspace/core/data/request/EXECUTE'),
|
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 */
|
/* tslint:disable:max-classes-per-file */
|
||||||
@@ -43,7 +45,10 @@ export class RequestExecuteAction implements Action {
|
|||||||
*/
|
*/
|
||||||
export class RequestCompleteAction implements Action {
|
export class RequestCompleteAction implements Action {
|
||||||
type = RequestActionTypes.COMPLETE;
|
type = RequestActionTypes.COMPLETE;
|
||||||
payload: string;
|
payload: {
|
||||||
|
uuid: string,
|
||||||
|
response: RestResponse
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new RequestCompleteAction
|
* Create a new RequestCompleteAction
|
||||||
@@ -51,10 +56,32 @@ export class RequestCompleteAction implements Action {
|
|||||||
* @param uuid
|
* @param uuid
|
||||||
* the request's uuid
|
* the request's uuid
|
||||||
*/
|
*/
|
||||||
constructor(uuid: string) {
|
constructor(uuid: string, response: RestResponse) {
|
||||||
this.payload = uuid;
|
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 */
|
/* tslint:enable:max-classes-per-file */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,4 +90,5 @@ export class RequestCompleteAction implements Action {
|
|||||||
export type RequestAction
|
export type RequestAction
|
||||||
= RequestConfigureAction
|
= RequestConfigureAction
|
||||||
| RequestExecuteAction
|
| RequestExecuteAction
|
||||||
| RequestCompleteAction;
|
| RequestCompleteAction
|
||||||
|
| ResetResponseTimestampsAction;
|
||||||
|
@@ -1,30 +1,33 @@
|
|||||||
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
import {of as observableOf, Observable } from 'rxjs';
|
|
||||||
import { Inject, Injectable, Injector } from '@angular/core';
|
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 { Actions, Effect, ofType } from '@ngrx/effects';
|
||||||
|
|
||||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
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 { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
|
|
||||||
import { DSpaceRESTv2Service } from '../dspace-rest-v2/dspace-rest-v2.service';
|
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 { RequestError, RestRequest } from './request.models';
|
||||||
import { RequestEntry } from './request.reducer';
|
import { RequestEntry } from './request.reducer';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||||
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
|
import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory';
|
||||||
import { catchError, flatMap, map, take, tap } from 'rxjs/operators';
|
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) =>
|
export const addToResponseCacheAndCompleteAction = (request: RestRequest, envConfig: GlobalConfig) =>
|
||||||
(source: Observable<ErrorResponse>): Observable<RequestCompleteAction> =>
|
(source: Observable<RestResponse>): Observable<RequestCompleteAction> =>
|
||||||
source.pipe(
|
source.pipe(
|
||||||
tap((response: RestResponse) => responseCache.add(request.href, response, request.responseMsToLive ? request.responseMsToLive : envConfig.cache.msToLive.default)),
|
map((response: RestResponse) => {
|
||||||
map((response: RestResponse) => new RequestCompleteAction(request.uuid))
|
return new RequestCompleteAction(request.uuid, response)
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -46,20 +49,32 @@ export class RequestEffects {
|
|||||||
}
|
}
|
||||||
return this.restApi.request(request.method, request.href, body, request.options).pipe(
|
return this.restApi.request(request.method, request.href, body, request.options).pipe(
|
||||||
map((data: DSpaceRESTV2Response) => this.injector.get(request.getResponseParser()).parse(request, data)),
|
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(
|
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(
|
constructor(
|
||||||
@Inject(GLOBAL_CONFIG) private EnvConfig: GlobalConfig,
|
@Inject(GLOBAL_CONFIG) private EnvConfig: GlobalConfig,
|
||||||
private actions$: Actions,
|
private actions$: Actions,
|
||||||
private restApi: DSpaceRESTv2Service,
|
private restApi: DSpaceRESTv2Service,
|
||||||
private injector: Injector,
|
private injector: Injector,
|
||||||
private responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService
|
protected requestService: RequestService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
@@ -14,32 +14,36 @@ import { BrowseItemsResponseParsingService } from './browse-items-response-parsi
|
|||||||
|
|
||||||
/* tslint:disable:max-classes-per-file */
|
/* tslint:disable:max-classes-per-file */
|
||||||
|
|
||||||
|
|
||||||
export abstract class RestRequest {
|
export abstract class RestRequest {
|
||||||
|
public responseMsToLive = 0;
|
||||||
constructor(
|
constructor(
|
||||||
public uuid: string,
|
public uuid: string,
|
||||||
public href: string,
|
public href: string,
|
||||||
public method: RestRequestMethod = RestRequestMethod.GET,
|
public method: RestRequestMethod = RestRequestMethod.GET,
|
||||||
public body?: any,
|
public body?: any,
|
||||||
public options?: HttpOptions,
|
public options?: HttpOptions,
|
||||||
public responseMsToLive?: number
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
||||||
return DSOResponseParsingService;
|
return DSOResponseParsingService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get toCache(): boolean {
|
||||||
|
return this.responseMsToLive > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetRequest extends RestRequest {
|
export class GetRequest extends RestRequest {
|
||||||
|
public responseMsToLive = 60 * 15 * 1000;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public uuid: string,
|
public uuid: string,
|
||||||
public href: string,
|
public href: string,
|
||||||
public body?: any,
|
public body?: any,
|
||||||
public options?: HttpOptions,
|
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;
|
return IntegrationResponseParsingService;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RequestError extends Error {
|
export class RequestError extends Error {
|
||||||
statusText: string;
|
statusText: string;
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
RequestActionTypes, RequestAction, RequestConfigureAction,
|
RequestActionTypes, RequestAction, RequestConfigureAction,
|
||||||
RequestExecuteAction, RequestCompleteAction
|
RequestExecuteAction, RequestCompleteAction, ResetResponseTimestampsAction
|
||||||
} from './request.actions';
|
} from './request.actions';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
|
import { RestResponse } from '../cache/response.models';
|
||||||
|
|
||||||
export class RequestEntry {
|
export class RequestEntry {
|
||||||
request: RestRequest;
|
request: RestRequest;
|
||||||
requestPending: boolean;
|
requestPending: boolean;
|
||||||
responsePending: boolean;
|
responsePending: boolean;
|
||||||
completed: boolean;
|
completed: boolean;
|
||||||
|
response: RestResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RequestState {
|
export interface RequestState {
|
||||||
@@ -32,6 +34,9 @@ export function requestReducer(state = initialState, action: RequestAction): Req
|
|||||||
case RequestActionTypes.COMPLETE: {
|
case RequestActionTypes.COMPLETE: {
|
||||||
return completeRequest(state, action as RequestCompleteAction);
|
return completeRequest(state, action as RequestCompleteAction);
|
||||||
}
|
}
|
||||||
|
case RequestActionTypes.RESET_TIMESTAMPS: {
|
||||||
|
return resetResponseTimestamps(state, action as ResetResponseTimestampsAction);
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return state;
|
return state;
|
||||||
@@ -45,7 +50,7 @@ function configureRequest(state: RequestState, action: RequestConfigureAction):
|
|||||||
request: action.payload,
|
request: action.payload,
|
||||||
requestPending: true,
|
requestPending: true,
|
||||||
responsePending: false,
|
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
|
* the new state, with the response added to the request
|
||||||
*/
|
*/
|
||||||
function completeRequest(state: RequestState, action: RequestCompleteAction): RequestState {
|
function completeRequest(state: RequestState, action: RequestCompleteAction): RequestState {
|
||||||
return Object.assign({}, state, {
|
const time = new Date().getTime();
|
||||||
[action.payload]: Object.assign({}, state[action.payload], {
|
|
||||||
|
const ob = Object.assign({}, state, {
|
||||||
|
[action.payload.uuid]: Object.assign({}, state[action.payload.uuid], {
|
||||||
responsePending: false,
|
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 { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { getMockObjectCacheService } from '../../shared/mocks/mock-object-cache.service';
|
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 { defaultUUID, getMockUUIDService } from '../../shared/mocks/mock-uuid.service';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { CoreState } from '../core.reducers';
|
import { CoreState } from '../core.reducers';
|
||||||
import { UUIDService } from '../shared/uuid.service';
|
import { UUIDService } from '../shared/uuid.service';
|
||||||
import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
|
import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
|
||||||
@@ -29,7 +27,6 @@ describe('RequestService', () => {
|
|||||||
let service: RequestService;
|
let service: RequestService;
|
||||||
let serviceAsAny: any;
|
let serviceAsAny: any;
|
||||||
let objectCache: ObjectCacheService;
|
let objectCache: ObjectCacheService;
|
||||||
let responseCache: ResponseCacheService;
|
|
||||||
let uuidService: UUIDService;
|
let uuidService: UUIDService;
|
||||||
let store: Store<CoreState>;
|
let store: Store<CoreState>;
|
||||||
|
|
||||||
@@ -49,7 +46,6 @@ describe('RequestService', () => {
|
|||||||
objectCache = getMockObjectCacheService();
|
objectCache = getMockObjectCacheService();
|
||||||
(objectCache.hasBySelfLink as any).and.returnValue(false);
|
(objectCache.hasBySelfLink as any).and.returnValue(false);
|
||||||
|
|
||||||
responseCache = getMockResponseCacheService();
|
|
||||||
(responseCache.has as any).and.returnValue(false);
|
(responseCache.has as any).and.returnValue(false);
|
||||||
(responseCache.get as any).and.returnValue(observableOf(undefined));
|
(responseCache.get as any).and.returnValue(observableOf(undefined));
|
||||||
|
|
||||||
@@ -65,7 +61,6 @@ describe('RequestService', () => {
|
|||||||
|
|
||||||
service = new RequestService(
|
service = new RequestService(
|
||||||
objectCache,
|
objectCache,
|
||||||
responseCache,
|
|
||||||
uuidService,
|
uuidService,
|
||||||
store
|
store
|
||||||
);
|
);
|
||||||
|
@@ -1,14 +1,23 @@
|
|||||||
import { Observable, merge as observableMerge } from 'rxjs';
|
import { merge as observableMerge, Observable, of as observableOf } from 'rxjs';
|
||||||
import { filter, first, map, mergeMap, partition, take } from 'rxjs/operators';
|
import {
|
||||||
|
filter,
|
||||||
|
find,
|
||||||
|
first,
|
||||||
|
map,
|
||||||
|
mergeMap,
|
||||||
|
reduce,
|
||||||
|
startWith,
|
||||||
|
switchMap,
|
||||||
|
take,
|
||||||
|
tap
|
||||||
|
} from 'rxjs/operators';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
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 { CacheableObject } from '../cache/object-cache.reducer';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { DSOSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
import { DSOSuccessResponse, RestResponse } from '../cache/response.models';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { coreSelector, CoreState } from '../core.reducers';
|
import { coreSelector, CoreState } from '../core.reducers';
|
||||||
import { IndexName } from '../index/index.reducer';
|
import { IndexName } from '../index/index.reducer';
|
||||||
import { pathSelector } from '../shared/selectors';
|
import { pathSelector } from '../shared/selectors';
|
||||||
@@ -19,13 +28,13 @@ import { GetRequest, RestRequest } from './request.models';
|
|||||||
import { RequestEntry } from './request.reducer';
|
import { RequestEntry } from './request.reducer';
|
||||||
import { CommitSSBAction } from '../cache/server-sync-buffer.actions';
|
import { CommitSSBAction } from '../cache/server-sync-buffer.actions';
|
||||||
import { RestRequestMethod } from './rest-request-method';
|
import { RestRequestMethod } from './rest-request-method';
|
||||||
|
import { getResponseFromEntry } from '../shared/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RequestService {
|
export class RequestService {
|
||||||
private requestsOnTheirWayToTheStore: string[] = [];
|
private requestsOnTheirWayToTheStore: string[] = [];
|
||||||
|
|
||||||
constructor(private objectCache: ObjectCacheService,
|
constructor(private objectCache: ObjectCacheService,
|
||||||
private responseCache: ResponseCacheService,
|
|
||||||
private uuidService: UUIDService,
|
private uuidService: UUIDService,
|
||||||
private store: Store<CoreState>) {
|
private store: Store<CoreState>) {
|
||||||
}
|
}
|
||||||
@@ -83,23 +92,27 @@ export class RequestService {
|
|||||||
|
|
||||||
private isCachedOrPending(request: GetRequest) {
|
private isCachedOrPending(request: GetRequest) {
|
||||||
let isCached = this.objectCache.hasBySelfLink(request.href);
|
let isCached = this.objectCache.hasBySelfLink(request.href);
|
||||||
if (!isCached && this.responseCache.has(request.href)) {
|
const responses: Observable<RestResponse> = this.isReusable(request.uuid).pipe(
|
||||||
const responses = this.responseCache.get(request.href).pipe(
|
filter((reusable: boolean) => !isCached && reusable),
|
||||||
take(1),
|
switchMap(() => {
|
||||||
map((entry: ResponseCacheEntry) => entry.response)
|
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 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(
|
const dsoSuccessResponses = responses.pipe(
|
||||||
filter((response) => response.isSuccessful && hasValue((response as DSOSuccessResponse).resourceSelfLinks)),
|
filter((response) => response.isSuccessful && hasValue((response as DSOSuccessResponse).resourceSelfLinks)),
|
||||||
map((response: DSOSuccessResponse) => response.resourceSelfLinks),
|
map((response: DSOSuccessResponse) => response.resourceSelfLinks),
|
||||||
map((resourceSelfLinks: string[]) => resourceSelfLinks
|
map((resourceSelfLinks: string[]) => resourceSelfLinks
|
||||||
.every((selfLink) => this.objectCache.hasBySelfLink(selfLink))
|
.every((selfLink) => this.objectCache.hasBySelfLink(selfLink))
|
||||||
));
|
));
|
||||||
const otherSuccessResponses = responses.pipe(filter((response) => response.isSuccessful && !hasValue((response as DSOSuccessResponse).resourceSelfLinks)), map(() => true));
|
const otherSuccessResponses = responses.pipe(filter((response) => response.isSuccessful && !hasValue((response as DSOSuccessResponse).resourceSelfLinks)), map(() => true));
|
||||||
|
|
||||||
|
observableMerge(errorResponses, otherSuccessResponses, dsoSuccessResponses).subscribe((c) => isCached = c);
|
||||||
|
|
||||||
observableMerge(errorResponses, otherSuccessResponses, dsoSuccessResponses).subscribe((c) => isCached = c);
|
|
||||||
}
|
|
||||||
const isPending = this.isPending(request);
|
const isPending = this.isPending(request);
|
||||||
return isCached || isPending;
|
return isCached || isPending;
|
||||||
}
|
}
|
||||||
@@ -129,4 +142,34 @@ export class RequestService {
|
|||||||
commit(method?: RestRequestMethod) {
|
commit(method?: RestRequestMethod) {
|
||||||
this.store.dispatch(new CommitSSBAction(method))
|
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 { 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 { DSOResponseParsingService } from './dso-response-parsing.service';
|
||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from './parsing.service';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { IntegrationService } from './integration.service';
|
import { IntegrationService } from './integration.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
@@ -11,7 +10,6 @@ export class AuthorityService extends IntegrationService {
|
|||||||
protected browseEndpoint = 'entries';
|
protected browseEndpoint = 'entries';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected halService: HALEndpointService) {
|
protected halService: HALEndpointService) {
|
||||||
super();
|
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 { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
|
@@ -6,7 +6,7 @@ import {
|
|||||||
ErrorResponse,
|
ErrorResponse,
|
||||||
IntegrationSuccessResponse,
|
IntegrationSuccessResponse,
|
||||||
RestResponse
|
RestResponse
|
||||||
} from '../cache/response-cache.models';
|
} from '../cache/response.models';
|
||||||
import { isNotEmpty } from '../../shared/empty.util';
|
import { isNotEmpty } from '../../shared/empty.util';
|
||||||
import { IntegrationObjectFactory } from './integration-object-factory';
|
import { IntegrationObjectFactory } from './integration-object-factory';
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { cold, getTestScheduler } from 'jasmine-marbles';
|
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
|
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { IntegrationRequest } from '../data/request.models';
|
import { IntegrationRequest } from '../data/request.models';
|
||||||
@@ -18,7 +17,6 @@ class TestService extends IntegrationService {
|
|||||||
protected browseEndpoint = BROWSE;
|
protected browseEndpoint = BROWSE;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected responseCache: ResponseCacheService,
|
|
||||||
protected requestService: RequestService,
|
protected requestService: RequestService,
|
||||||
protected halService: HALEndpointService) {
|
protected halService: HALEndpointService) {
|
||||||
super();
|
super();
|
||||||
@@ -28,7 +26,6 @@ class TestService extends IntegrationService {
|
|||||||
describe('IntegrationService', () => {
|
describe('IntegrationService', () => {
|
||||||
let scheduler: TestScheduler;
|
let scheduler: TestScheduler;
|
||||||
let service: TestService;
|
let service: TestService;
|
||||||
let responseCache: ResponseCacheService;
|
|
||||||
let requestService: RequestService;
|
let requestService: RequestService;
|
||||||
let halService: any;
|
let halService: any;
|
||||||
let findOptions: IntegrationSearchOptions;
|
let findOptions: IntegrationSearchOptions;
|
||||||
@@ -43,24 +40,14 @@ describe('IntegrationService', () => {
|
|||||||
|
|
||||||
findOptions = new IntegrationSearchOptions(uuid, name, metadata);
|
findOptions = new IntegrationSearchOptions(uuid, name, metadata);
|
||||||
|
|
||||||
function initMockResponseCacheService(isSuccessful: boolean): ResponseCacheService {
|
function initTestService(): TestService {
|
||||||
return jasmine.createSpyObj('responseCache', {
|
|
||||||
get: cold('c-', {
|
|
||||||
c: {response: {isSuccessful}}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initTestService(): TestService {
|
|
||||||
return new TestService(
|
return new TestService(
|
||||||
responseCache,
|
|
||||||
requestService,
|
requestService,
|
||||||
halService
|
halService
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
responseCache = initMockResponseCacheService(true);
|
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
scheduler = getTestScheduler();
|
scheduler = getTestScheduler();
|
||||||
halService = new HALEndpointServiceStub(integrationEndpoint);
|
halService = new HALEndpointServiceStub(integrationEndpoint);
|
||||||
|
@@ -1,27 +1,26 @@
|
|||||||
import { Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
|
import { Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
import { IntegrationSuccessResponse } from '../cache/response.models';
|
||||||
import { IntegrationSuccessResponse } from '../cache/response-cache.models';
|
|
||||||
import { GetRequest, IntegrationRequest } from '../data/request.models';
|
import { GetRequest, IntegrationRequest } from '../data/request.models';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
|
||||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { IntegrationData } from './integration-data';
|
import { IntegrationData } from './integration-data';
|
||||||
import { IntegrationSearchOptions } from './models/integration-options.model';
|
import { IntegrationSearchOptions } from './models/integration-options.model';
|
||||||
|
import { RequestEntry } from '../data/request.reducer';
|
||||||
|
import { getResponseFromEntry } from '../shared/operators';
|
||||||
|
|
||||||
export abstract class IntegrationService {
|
export abstract class IntegrationService {
|
||||||
protected request: IntegrationRequest;
|
protected request: IntegrationRequest;
|
||||||
protected abstract responseCache: ResponseCacheService;
|
|
||||||
protected abstract requestService: RequestService;
|
protected abstract requestService: RequestService;
|
||||||
protected abstract linkPath: string;
|
protected abstract linkPath: string;
|
||||||
protected abstract browseEndpoint: string;
|
protected abstract browseEndpoint: string;
|
||||||
protected abstract halService: HALEndpointService;
|
protected abstract halService: HALEndpointService;
|
||||||
|
|
||||||
protected getData(request: GetRequest): Observable<IntegrationData> {
|
protected getData(request: GetRequest): Observable<IntegrationData> {
|
||||||
return this.responseCache.get(request.href).pipe(
|
return this.requestService.getByHref(request.href).pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
mergeMap((response) => {
|
mergeMap((response) => {
|
||||||
if (response.isSuccessful && isNotEmpty(response)) {
|
if (response.isSuccessful && isNotEmpty(response)) {
|
||||||
const dataResponse = response as IntegrationSuccessResponse;
|
const dataResponse = response as IntegrationSuccessResponse;
|
||||||
return observableOf(new IntegrationData(dataResponse.pageInfo, dataResponse.dataDefinition));
|
return observableOf(new IntegrationData(dataResponse.pageInfo, dataResponse.dataDefinition));
|
||||||
|
@@ -23,7 +23,6 @@ import { ItemDataService } from '../data/item-data.service';
|
|||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
|
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
@@ -62,7 +61,6 @@ describe('MetadataService', () => {
|
|||||||
let store: Store<CoreState>;
|
let store: Store<CoreState>;
|
||||||
|
|
||||||
let objectCacheService: ObjectCacheService;
|
let objectCacheService: ObjectCacheService;
|
||||||
let responseCacheService: ResponseCacheService;
|
|
||||||
let requestService: RequestService;
|
let requestService: RequestService;
|
||||||
let uuidService: UUIDService;
|
let uuidService: UUIDService;
|
||||||
let remoteDataBuildService: RemoteDataBuildService;
|
let remoteDataBuildService: RemoteDataBuildService;
|
||||||
@@ -82,10 +80,9 @@ describe('MetadataService', () => {
|
|||||||
spyOn(store, 'dispatch');
|
spyOn(store, 'dispatch');
|
||||||
|
|
||||||
objectCacheService = new ObjectCacheService(store);
|
objectCacheService = new ObjectCacheService(store);
|
||||||
responseCacheService = new ResponseCacheService(store);
|
|
||||||
uuidService = new UUIDService();
|
uuidService = new UUIDService();
|
||||||
requestService = new RequestService(objectCacheService, responseCacheService, uuidService, store);
|
requestService = new RequestService(objectCacheService, uuidService, store);
|
||||||
remoteDataBuildService = new RemoteDataBuildService(objectCacheService, responseCacheService, requestService);
|
remoteDataBuildService = new RemoteDataBuildService(objectCacheService, requestService);
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -108,7 +105,6 @@ describe('MetadataService', () => {
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ObjectCacheService, useValue: objectCacheService },
|
{ provide: ObjectCacheService, useValue: objectCacheService },
|
||||||
{ provide: ResponseCacheService, useValue: responseCacheService },
|
|
||||||
{ provide: RequestService, useValue: requestService },
|
{ provide: RequestService, useValue: requestService },
|
||||||
{ provide: RemoteDataBuildService, useValue: remoteDataBuildService },
|
{ provide: RemoteDataBuildService, useValue: remoteDataBuildService },
|
||||||
{ provide: GLOBAL_CONFIG, useValue: ENV_CONFIG },
|
{ provide: GLOBAL_CONFIG, useValue: ENV_CONFIG },
|
||||||
|
@@ -1,24 +1,21 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { RegistryService } from './registry.service';
|
import { RegistryService } from './registry.service';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { Observable, of as observableOf, combineLatest as observableCombineLatest } from 'rxjs';
|
import { Observable, of as observableOf, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
|
||||||
import { RequestEntry } from '../data/request.reducer';
|
import { RequestEntry } from '../data/request.reducer';
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import { PageInfo } from '../shared/page-info.model';
|
import { PageInfo } from '../shared/page-info.model';
|
||||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||||
import { getMockResponseCacheService } from '../../shared/mocks/mock-response-cache.service';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
RegistryBitstreamformatsSuccessResponse,
|
RegistryBitstreamformatsSuccessResponse,
|
||||||
RegistryMetadatafieldsSuccessResponse,
|
RegistryMetadatafieldsSuccessResponse,
|
||||||
RegistryMetadataschemasSuccessResponse
|
RegistryMetadataschemasSuccessResponse
|
||||||
} from '../cache/response-cache.models';
|
} from '../cache/response.models';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { RegistryMetadataschemasResponse } from './registry-metadataschemas-response.model';
|
import { RegistryMetadataschemasResponse } from './registry-metadataschemas-response.model';
|
||||||
import { RegistryMetadatafieldsResponse } from './registry-metadatafields-response.model';
|
import { RegistryMetadatafieldsResponse } from './registry-metadatafields-response.model';
|
||||||
@@ -146,7 +143,6 @@ describe('RegistryService', () => {
|
|||||||
DummyComponent
|
DummyComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ResponseCacheService, useValue: getMockResponseCacheService() },
|
|
||||||
{ provide: RequestService, useValue: getMockRequestService() },
|
{ provide: RequestService, useValue: getMockRequestService() },
|
||||||
{ provide: RemoteDataBuildService, useValue: rdbStub },
|
{ provide: RemoteDataBuildService, useValue: rdbStub },
|
||||||
{ provide: HALEndpointService, useValue: halServiceStub },
|
{ provide: HALEndpointService, useValue: halServiceStub },
|
||||||
|
@@ -6,29 +6,29 @@ import { PageInfo } from '../shared/page-info.model';
|
|||||||
import { MetadataSchema } from '../metadata/metadataschema.model';
|
import { MetadataSchema } from '../metadata/metadataschema.model';
|
||||||
import { MetadataField } from '../metadata/metadatafield.model';
|
import { MetadataField } from '../metadata/metadatafield.model';
|
||||||
import { BitstreamFormat } from './mock-bitstream-format.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 { GetRequest, RestRequest } from '../data/request.models';
|
||||||
import { GenericConstructor } from '../shared/generic-constructor';
|
import { GenericConstructor } from '../shared/generic-constructor';
|
||||||
import { ResponseParsingService } from '../data/parsing.service';
|
import { ResponseParsingService } from '../data/parsing.service';
|
||||||
import { RegistryMetadataschemasResponseParsingService } from '../data/registry-metadataschemas-response-parsing.service';
|
import { RegistryMetadataschemasResponseParsingService } from '../data/registry-metadataschemas-response-parsing.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { RegistryMetadataschemasResponse } from './registry-metadataschemas-response.model';
|
import { RegistryMetadataschemasResponse } from './registry-metadataschemas-response.model';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
|
||||||
import {
|
import {
|
||||||
RegistryBitstreamformatsSuccessResponse,
|
RegistryBitstreamformatsSuccessResponse,
|
||||||
RegistryMetadatafieldsSuccessResponse,
|
RegistryMetadatafieldsSuccessResponse,
|
||||||
RegistryMetadataschemasSuccessResponse
|
RegistryMetadataschemasSuccessResponse
|
||||||
} from '../cache/response-cache.models';
|
} from '../cache/response.models';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { RegistryMetadatafieldsResponseParsingService } from '../data/registry-metadatafields-response-parsing.service';
|
import { RegistryMetadatafieldsResponseParsingService } from '../data/registry-metadatafields-response-parsing.service';
|
||||||
import { RegistryMetadatafieldsResponse } from './registry-metadatafields-response.model';
|
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 { URLCombiner } from '../url-combiner/url-combiner';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { RegistryBitstreamformatsResponseParsingService } from '../data/registry-bitstreamformats-response-parsing.service';
|
import { RegistryBitstreamformatsResponseParsingService } from '../data/registry-bitstreamformats-response-parsing.service';
|
||||||
import { RegistryBitstreamformatsResponse } from './registry-bitstreamformats-response.model';
|
import { RegistryBitstreamformatsResponse } from './registry-bitstreamformats-response.model';
|
||||||
|
import { RequestEntry } from '../data/request.reducer';
|
||||||
|
import { getResponseFromEntry } from '../shared/operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RegistryService {
|
export class RegistryService {
|
||||||
@@ -37,8 +37,7 @@ export class RegistryService {
|
|||||||
private metadataFieldsPath = 'metadatafields';
|
private metadataFieldsPath = 'metadatafields';
|
||||||
private bitstreamFormatsPath = 'bitstreamformats';
|
private bitstreamFormatsPath = 'bitstreamformats';
|
||||||
|
|
||||||
constructor(protected responseCache: ResponseCacheService,
|
constructor(protected requestService: RequestService,
|
||||||
protected requestService: RequestService,
|
|
||||||
private rdb: RemoteDataBuildService,
|
private rdb: RemoteDataBuildService,
|
||||||
private halService: HALEndpointService) {
|
private halService: HALEndpointService) {
|
||||||
|
|
||||||
@@ -51,12 +50,8 @@ export class RegistryService {
|
|||||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||||
);
|
);
|
||||||
|
|
||||||
const responseCacheObs = requestObs.pipe(
|
const rmrObs: Observable<RegistryMetadataschemasResponse> = requestEntryObs.pipe(
|
||||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
getResponseFromEntry(),
|
||||||
);
|
|
||||||
|
|
||||||
const rmrObs: Observable<RegistryMetadataschemasResponse> = responseCacheObs.pipe(
|
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
|
||||||
map((response: RegistryMetadataschemasSuccessResponse) => response.metadataschemasResponse)
|
map((response: RegistryMetadataschemasSuccessResponse) => response.metadataschemasResponse)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -64,8 +59,8 @@ export class RegistryService {
|
|||||||
map((rmr: RegistryMetadataschemasResponse) => rmr.metadataschemas)
|
map((rmr: RegistryMetadataschemasResponse) => rmr.metadataschemas)
|
||||||
);
|
);
|
||||||
|
|
||||||
const pageInfoObs: Observable<PageInfo> = responseCacheObs.pipe(
|
const pageInfoObs: Observable<PageInfo> = requestEntryObs.pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
map((response: RegistryMetadataschemasSuccessResponse) => response.pageInfo)
|
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>> {
|
public getMetadataSchemaByName(schemaName: string): Observable<RemoteData<MetadataSchema>> {
|
||||||
@@ -90,12 +85,8 @@ export class RegistryService {
|
|||||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||||
);
|
);
|
||||||
|
|
||||||
const responseCacheObs = requestObs.pipe(
|
const rmrObs: Observable<RegistryMetadataschemasResponse> = requestEntryObs.pipe(
|
||||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
getResponseFromEntry(),
|
||||||
);
|
|
||||||
|
|
||||||
const rmrObs: Observable<RegistryMetadataschemasResponse> = responseCacheObs.pipe(
|
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
|
||||||
map((response: RegistryMetadataschemasSuccessResponse) => response.metadataschemasResponse)
|
map((response: RegistryMetadataschemasSuccessResponse) => response.metadataschemasResponse)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -104,7 +95,7 @@ export class RegistryService {
|
|||||||
map((metadataSchemas: MetadataSchema[]) => metadataSchemas.filter((value) => value.prefix === schemaName)[0])
|
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>>> {
|
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))
|
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||||
);
|
);
|
||||||
|
|
||||||
const responseCacheObs = requestObs.pipe(
|
const rmrObs: Observable<RegistryMetadatafieldsResponse> = requestEntryObs.pipe(
|
||||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
getResponseFromEntry(),
|
||||||
);
|
|
||||||
|
|
||||||
const rmrObs: Observable<RegistryMetadatafieldsResponse> = responseCacheObs.pipe(
|
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
|
||||||
map((response: RegistryMetadatafieldsSuccessResponse) => response.metadatafieldsResponse)
|
map((response: RegistryMetadatafieldsSuccessResponse) => response.metadatafieldsResponse)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -128,8 +115,9 @@ export class RegistryService {
|
|||||||
map((metadataFields: MetadataField[]) => metadataFields.filter((field) => field.schema.id === schema.id))
|
map((metadataFields: MetadataField[]) => metadataFields.filter((field) => field.schema.id === schema.id))
|
||||||
);
|
);
|
||||||
|
|
||||||
const pageInfoObs: Observable<PageInfo> = responseCacheObs.pipe(
|
const pageInfoObs: Observable<PageInfo> = requestEntryObs.pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
|
|
||||||
map((response: RegistryMetadatafieldsSuccessResponse) => response.pageInfo)
|
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>>> {
|
public getBitstreamFormats(pagination: PaginationComponentOptions): Observable<RemoteData<PaginatedList<BitstreamFormat>>> {
|
||||||
@@ -149,12 +137,8 @@ export class RegistryService {
|
|||||||
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||||
);
|
);
|
||||||
|
|
||||||
const responseCacheObs = requestObs.pipe(
|
const rbrObs: Observable<RegistryBitstreamformatsResponse> = requestEntryObs.pipe(
|
||||||
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
getResponseFromEntry(),
|
||||||
);
|
|
||||||
|
|
||||||
const rbrObs: Observable<RegistryBitstreamformatsResponse> = responseCacheObs.pipe(
|
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
|
||||||
map((response: RegistryBitstreamformatsSuccessResponse) => response.bitstreamformatsResponse)
|
map((response: RegistryBitstreamformatsSuccessResponse) => response.bitstreamformatsResponse)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -162,8 +146,8 @@ export class RegistryService {
|
|||||||
map((rbr: RegistryBitstreamformatsResponse) => rbr.bitstreamformats)
|
map((rbr: RegistryBitstreamformatsResponse) => rbr.bitstreamformats)
|
||||||
);
|
);
|
||||||
|
|
||||||
const pageInfoObs: Observable<PageInfo> = responseCacheObs.pipe(
|
const pageInfoObs: Observable<PageInfo> = requestEntryObs.pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
map((response: RegistryBitstreamformatsSuccessResponse) => response.pageInfo)
|
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> {
|
private getMetadataSchemasRequestObs(pagination: PaginationComponentOptions): Observable<RestRequest> {
|
||||||
|
@@ -1,14 +1,12 @@
|
|||||||
import { cold, hot } from 'jasmine-marbles';
|
import { cold, hot } from 'jasmine-marbles';
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { HALEndpointService } from './hal-endpoint.service';
|
import { HALEndpointService } from './hal-endpoint.service';
|
||||||
import { EndpointMapRequest } from '../data/request.models';
|
import { EndpointMapRequest } from '../data/request.models';
|
||||||
|
|
||||||
describe('HALEndpointService', () => {
|
describe('HALEndpointService', () => {
|
||||||
let service: HALEndpointService;
|
let service: HALEndpointService;
|
||||||
let responseCache: ResponseCacheService;
|
|
||||||
let requestService: RequestService;
|
let requestService: RequestService;
|
||||||
let envConfig: GlobalConfig;
|
let envConfig: GlobalConfig;
|
||||||
|
|
||||||
@@ -19,14 +17,6 @@ describe('HALEndpointService', () => {
|
|||||||
|
|
||||||
describe('getRootEndpointMap', () => {
|
describe('getRootEndpointMap', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
responseCache = jasmine.createSpyObj('responseCache', {
|
|
||||||
get: hot('a-', {
|
|
||||||
a: {
|
|
||||||
response: { endpointMap: endpointMap }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
|
|
||||||
envConfig = {
|
envConfig = {
|
||||||
@@ -34,7 +24,6 @@ describe('HALEndpointService', () => {
|
|||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
service = new HALEndpointService(
|
service = new HALEndpointService(
|
||||||
responseCache,
|
|
||||||
requestService,
|
requestService,
|
||||||
envConfig
|
envConfig
|
||||||
);
|
);
|
||||||
@@ -60,12 +49,6 @@ describe('HALEndpointService', () => {
|
|||||||
envConfig = {
|
envConfig = {
|
||||||
rest: { baseUrl: 'https://rest.api/' }
|
rest: { baseUrl: 'https://rest.api/' }
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
service = new HALEndpointService(
|
|
||||||
responseCache,
|
|
||||||
requestService,
|
|
||||||
envConfig
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the endpoint URL for the service\'s linkPath', () => {
|
it('should return the endpoint URL for the service\'s linkPath', () => {
|
||||||
@@ -89,7 +72,6 @@ describe('HALEndpointService', () => {
|
|||||||
describe('isEnabledOnRestApi', () => {
|
describe('isEnabledOnRestApi', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service = new HALEndpointService(
|
service = new HALEndpointService(
|
||||||
responseCache,
|
|
||||||
requestService,
|
requestService,
|
||||||
envConfig
|
envConfig
|
||||||
);
|
);
|
||||||
|
@@ -1,21 +1,27 @@
|
|||||||
import {of as observableOf, Observable } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
import {filter, distinctUntilChanged, map, flatMap, startWith, tap } from 'rxjs/operators';
|
import {
|
||||||
|
distinctUntilChanged,
|
||||||
|
filter,
|
||||||
|
flatMap,
|
||||||
|
map,
|
||||||
|
startWith,
|
||||||
|
switchMap,
|
||||||
|
tap
|
||||||
|
} from 'rxjs/operators';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||||
import { EndpointMap, EndpointMapSuccessResponse } from '../cache/response-cache.models';
|
|
||||||
import { EndpointMapRequest } from '../data/request.models';
|
import { EndpointMapRequest } from '../data/request.models';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util';
|
||||||
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
|
||||||
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
|
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
|
||||||
import { Inject, Injectable } from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { GLOBAL_CONFIG } from '../../../config';
|
import { GLOBAL_CONFIG } from '../../../config';
|
||||||
|
import { EndpointMap, EndpointMapSuccessResponse } from '../cache/response.models';
|
||||||
|
import { getResponseFromEntry } from './operators';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HALEndpointService {
|
export class HALEndpointService {
|
||||||
|
|
||||||
constructor(private responseCache: ResponseCacheService,
|
constructor(private requestService: RequestService,
|
||||||
private requestService: RequestService,
|
|
||||||
@Inject(GLOBAL_CONFIG) private EnvConfig: GlobalConfig) {
|
@Inject(GLOBAL_CONFIG) private EnvConfig: GlobalConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,12 +35,22 @@ export class HALEndpointService {
|
|||||||
|
|
||||||
private getEndpointMapAt(href): Observable<EndpointMap> {
|
private getEndpointMapAt(href): Observable<EndpointMap> {
|
||||||
const request = new EndpointMapRequest(this.requestService.generateRequestId(), href);
|
const request = new EndpointMapRequest(this.requestService.generateRequestId(), href);
|
||||||
this.requestService.configure(request);
|
|
||||||
return this.responseCache.get(request.href).pipe(
|
this.requestService.getByUUID(request.uuid).pipe(
|
||||||
map((entry: ResponseCacheEntry) => entry.response),
|
getResponseFromEntry(),
|
||||||
filter((response: EndpointMapSuccessResponse) => isNotEmpty(response)),
|
|
||||||
map((response: EndpointMapSuccessResponse) => response.endpointMap),
|
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> {
|
public getEndpoint(linkPath: string): Observable<string> {
|
||||||
@@ -48,7 +64,7 @@ export class HALEndpointService {
|
|||||||
let currentPath;
|
let currentPath;
|
||||||
const pipeArguments = path
|
const pipeArguments = path
|
||||||
.map((subPath: string, index: number) => [
|
.map((subPath: string, index: number) => [
|
||||||
flatMap((href: string) => this.getEndpointMapAt(href)),
|
switchMap((href: string) => this.getEndpointMapAt(href)),
|
||||||
map((endpointMap: EndpointMap) => {
|
map((endpointMap: EndpointMap) => {
|
||||||
if (hasValue(endpointMap) && hasValue(endpointMap[subPath])) {
|
if (hasValue(endpointMap) && hasValue(endpointMap[subPath])) {
|
||||||
currentPath = endpointMap[subPath];
|
currentPath = endpointMap[subPath];
|
||||||
|
@@ -1,17 +1,15 @@
|
|||||||
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||||
import { TestScheduler } from 'rxjs/testing';
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
import { getMockRequestService } from '../../shared/mocks/mock-request.service';
|
||||||
import { getMockResponseCacheService } from '../../shared/mocks/mock-response-cache.service';
|
import { GetRequest } from '../data/request.models';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { GetRequest, RestRequest } from '../data/request.models';
|
|
||||||
import { RequestEntry } from '../data/request.reducer';
|
import { RequestEntry } from '../data/request.reducer';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import {
|
import {
|
||||||
configureRequest,
|
configureRequest,
|
||||||
filterSuccessfulResponses, getRemoteDataPayload,
|
filterSuccessfulResponses,
|
||||||
getRequestFromSelflink, getResourceLinksFromResponse,
|
getRemoteDataPayload,
|
||||||
getResponseFromSelflink
|
getRequestFromSelflink,
|
||||||
|
getResourceLinksFromResponse,
|
||||||
} from './operators';
|
} from './operators';
|
||||||
|
|
||||||
describe('Core Module - RxJS 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', () => {
|
describe('filterSuccessfulResponses', () => {
|
||||||
it('should only return responses for which isSuccessful === true', () => {
|
it('should only return responses for which isSuccessful === true', () => {
|
||||||
const source = hot('abcde', testRCEs);
|
const source = hot('abcde', testRCEs);
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { filter, first, flatMap, map, tap } from 'rxjs/operators';
|
import { filter, first, flatMap, map, tap } from 'rxjs/operators';
|
||||||
import { hasValueOperator, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, hasValueOperator, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { DSOSuccessResponse } from '../cache/response-cache.models';
|
import { DSOSuccessResponse, RestResponse } from '../cache/response.models';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import { RestRequest } from '../data/request.models';
|
import { RestRequest } from '../data/request.models';
|
||||||
import { RequestEntry } from '../data/request.reducer';
|
import { RequestEntry } from '../data/request.reducer';
|
||||||
@@ -24,22 +22,25 @@ export const getRequestFromSelflink = (requestService: RequestService) =>
|
|||||||
hasValueOperator()
|
hasValueOperator()
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getResponseFromSelflink = (responseCache: ResponseCacheService) =>
|
export const filterSuccessfulResponses = () =>
|
||||||
(source: Observable<string>): Observable<ResponseCacheEntry> =>
|
(source: Observable<RequestEntry>): Observable<RestResponse> =>
|
||||||
source.pipe(
|
source.pipe(
|
||||||
flatMap((href: string) => responseCache.get(href)),
|
getResponseFromEntry(),
|
||||||
hasValueOperator()
|
filter((response: RestResponse) => response.isSuccessful === true),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const filterSuccessfulResponses = () =>
|
export const getResponseFromEntry = () =>
|
||||||
(source: Observable<ResponseCacheEntry>): Observable<ResponseCacheEntry> =>
|
(source: Observable<RequestEntry>): Observable<RestResponse> =>
|
||||||
source.pipe(filter((entry: ResponseCacheEntry) => entry.response.isSuccessful === true));
|
source.pipe(
|
||||||
|
filter((entry: RequestEntry) => hasValue(entry) && hasValue(entry.response)),
|
||||||
|
map((entry: RequestEntry) => entry.response)
|
||||||
|
);
|
||||||
|
|
||||||
export const getResourceLinksFromResponse = () =>
|
export const getResourceLinksFromResponse = () =>
|
||||||
(source: Observable<ResponseCacheEntry>): Observable<string[]> =>
|
(source: Observable<RequestEntry>): Observable<string[]> =>
|
||||||
source.pipe(
|
source.pipe(
|
||||||
filterSuccessfulResponses(),
|
filterSuccessfulResponses(),
|
||||||
map((entry: ResponseCacheEntry) => (entry.response as DSOSuccessResponse).resourceSelfLinks),
|
map((response: DSOSuccessResponse) => response.resourceSelfLinks),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const configureRequest = (requestService: RequestService) =>
|
export const configureRequest = (requestService: RequestService) =>
|
||||||
@@ -60,7 +61,7 @@ export const toDSpaceObjectListRD = () =>
|
|||||||
map((rd: RemoteData<PaginatedList<SearchResult<T>>>) => {
|
map((rd: RemoteData<PaginatedList<SearchResult<T>>>) => {
|
||||||
const dsoPage: T[] = rd.payload.page.map((searchResult: SearchResult<T>) => searchResult.dspaceObject);
|
const dsoPage: T[] = rd.payload.page.map((searchResult: SearchResult<T>) => searchResult.dspaceObject);
|
||||||
const payload = Object.assign(rd.payload, { page: dsoPage }) as PaginatedList<T>;
|
const payload = Object.assign(rd.payload, { page: dsoPage }) as PaginatedList<T>;
|
||||||
return Object.assign(rd, {payload: payload});
|
return Object.assign(rd, { payload: payload });
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -1,14 +1,13 @@
|
|||||||
import {of as observableOf, Observable } from 'rxjs';
|
import {of as observableOf, Observable } from 'rxjs';
|
||||||
import { map, take } from 'rxjs/operators';
|
import { map, take } from 'rxjs/operators';
|
||||||
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
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 { RemoteData } from '../../core/data/remote-data';
|
||||||
import { RequestEntry } from '../../core/data/request.reducer';
|
import { RequestEntry } from '../../core/data/request.reducer';
|
||||||
import { hasValue } from '../empty.util';
|
import { hasValue } from '../empty.util';
|
||||||
|
|
||||||
export function getMockRemoteDataBuildService(toRemoteDataObservable$?: Observable<RemoteData<any>>): RemoteDataBuildService {
|
export function getMockRemoteDataBuildService(toRemoteDataObservable$?: Observable<RemoteData<any>>): RemoteDataBuildService {
|
||||||
return {
|
return {
|
||||||
toRemoteDataObservable: (requestEntry$: Observable<RequestEntry>, responseCache$: Observable<ResponseCacheEntry>, payload$: Observable<any>) => {
|
toRemoteDataObservable: (requestEntry$: Observable<RequestEntry>, payload$: Observable<any>) => {
|
||||||
|
|
||||||
if (hasValue(toRemoteDataObservable$)) {
|
if (hasValue(toRemoteDataObservable$)) {
|
||||||
return 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 ngApp(req, res) {
|
||||||
|
|
||||||
function onHandleError(parentZoneDelegate, currentZone, targetZone, error) {
|
function onHandleError(parentZoneDelegate, currentZone, targetZone, error) {
|
||||||
|
console.error('Error:', error);
|
||||||
console.warn('Error in SSR, serving for direct CSR');
|
console.warn('Error in SSR, serving for direct CSR');
|
||||||
res.sendFile('index.csr.html', { root: './src' });
|
res.sendFile('index.csr.html', { root: './src' });
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user