mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-14 13:33:03 +00:00
turning search response in to remotedata
This commit is contained in:

committed by
Lotte Hofstede

parent
4c2cbc55e0
commit
461ba33e0d
@@ -1,12 +1,15 @@
|
|||||||
import { Inject, Injectable, OnDestroy } from '@angular/core';
|
import { Inject, Injectable, OnDestroy } from '@angular/core';
|
||||||
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
|
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { map } from 'rxjs/operators';
|
import { map, flatMap, tap, filter } from 'rxjs/operators';
|
||||||
import { ViewMode } from '../../+search-page/search-options.model';
|
import { ViewMode } from '../../+search-page/search-options.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';
|
||||||
|
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
|
||||||
|
import { NormalizedDSpaceObject } from '../../core/cache/models/normalized-dspace-object.model';
|
||||||
import { SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { RestResponse } from '../../core/cache/response-cache.models';
|
import { RestResponse, SearchSuccessResponse } from '../../core/cache/response-cache.models';
|
||||||
|
import { ResponseCacheEntry } from '../../core/cache/response-cache.reducer';
|
||||||
import { ResponseCacheService } from '../../core/cache/response-cache.service';
|
import { ResponseCacheService } from '../../core/cache/response-cache.service';
|
||||||
import { DebugResponseParsingService } from '../../core/data/debug-response-parsing.service';
|
import { DebugResponseParsingService } from '../../core/data/debug-response-parsing.service';
|
||||||
import { DSOResponseParsingService } from '../../core/data/dso-response-parsing.service';
|
import { DSOResponseParsingService } from '../../core/data/dso-response-parsing.service';
|
||||||
@@ -15,6 +18,7 @@ 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';
|
||||||
import { GetRequest, EndpointMapRequest, RestRequest } from '../../core/data/request.models';
|
import { GetRequest, EndpointMapRequest, RestRequest } from '../../core/data/request.models';
|
||||||
|
import { RequestEntry } from '../../core/data/request.reducer';
|
||||||
import { RequestService } from '../../core/data/request.service';
|
import { RequestService } from '../../core/data/request.service';
|
||||||
import { DSpaceRESTV2Response } from '../../core/dspace-rest-v2/dspace-rest-v2-response.model';
|
import { DSpaceRESTV2Response } from '../../core/dspace-rest-v2/dspace-rest-v2-response.model';
|
||||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
@@ -23,16 +27,19 @@ import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
|||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
import { Metadatum } from '../../core/shared/metadatum.model';
|
import { Metadatum } from '../../core/shared/metadatum.model';
|
||||||
import { PageInfo } from '../../core/shared/page-info.model';
|
import { PageInfo } from '../../core/shared/page-info.model';
|
||||||
|
import { URLCombiner } from '../../core/url-combiner/url-combiner';
|
||||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||||
import { ItemSearchResult } from '../../shared/object-collection/shared/item-search-result.model';
|
import { ItemSearchResult } from '../../shared/object-collection/shared/item-search-result.model';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { RouteService } from '../../shared/route.service';
|
import { RouteService } from '../../shared/route.service';
|
||||||
|
import { NormalizedSearchResult } from '../normalized-search-result.model';
|
||||||
import { SearchOptions } from '../search-options.model';
|
import { SearchOptions } from '../search-options.model';
|
||||||
import { SearchResult } from '../search-result.model';
|
import { SearchResult } from '../search-result.model';
|
||||||
import { FacetValue } from './facet-value.model';
|
import { FacetValue } from './facet-value.model';
|
||||||
import { FilterType } from './filter-type.model';
|
import { FilterType } from './filter-type.model';
|
||||||
import { SearchFilterConfig } from './search-filter-config.model';
|
import { SearchFilterConfig } from './search-filter-config.model';
|
||||||
import { SearchResponseParsingService } from '../../core/data/search-response-parsing.service';
|
import { SearchResponseParsingService } from '../../core/data/search-response-parsing.service';
|
||||||
|
import { SearchQueryResponse } from './search-query-response.model';
|
||||||
|
|
||||||
function shuffle(array: any[]) {
|
function shuffle(array: any[]) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
@@ -95,6 +102,7 @@ export class SearchService extends HALEndpointService implements OnDestroy {
|
|||||||
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||||
private routeService: RouteService,
|
private routeService: RouteService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
private rdb: RemoteDataBuildService,
|
||||||
private router: Router
|
private router: Router
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
@@ -108,18 +116,62 @@ export class SearchService extends HALEndpointService implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
search(query: string, scopeId?: string, searchOptions?: SearchOptions): Observable<RemoteData<Array<SearchResult<DSpaceObject>>>> {
|
search(query: string, scopeId?: string, searchOptions?: SearchOptions): Observable<RemoteData<Array<SearchResult<DSpaceObject>>>> {
|
||||||
const searchEndpointUrlObs = this.getEndpoint();
|
const requestObs = this.getEndpoint().pipe(
|
||||||
searchEndpointUrlObs.pipe(
|
|
||||||
map((url: string) => {
|
map((url: string) => {
|
||||||
|
const args: string[] = [];
|
||||||
|
|
||||||
|
if (isNotEmpty(query)) {
|
||||||
|
args.push(`query=${query}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotEmpty(scopeId)) {
|
||||||
|
args.push(`scope=${scopeId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotEmpty(args)) {
|
||||||
|
url = new URLCombiner(url, `?${args.join('&')}`).toString();
|
||||||
|
}
|
||||||
|
|
||||||
const request = new GetRequest(this.requestService.generateRequestId(), url);
|
const request = new GetRequest(this.requestService.generateRequestId(), url);
|
||||||
return Object.assign(request, {
|
return Object.assign(request, {
|
||||||
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
||||||
return SearchResponseParsingService;
|
return SearchResponseParsingService;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}),
|
||||||
|
tap((request: RestRequest) => this.requestService.configure(request)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const requestEntryObs = requestObs.pipe(
|
||||||
|
flatMap((request: RestRequest) => this.requestService.getByHref(request.href))
|
||||||
|
);
|
||||||
|
|
||||||
|
const responseCacheObs = requestObs.pipe(
|
||||||
|
flatMap((request: RestRequest) => this.responseCache.get(request.href))
|
||||||
|
);
|
||||||
|
|
||||||
|
const sqrObs = responseCacheObs.pipe(
|
||||||
|
map((entry: ResponseCacheEntry) => entry.response),
|
||||||
|
map((response: SearchSuccessResponse) => response.results)
|
||||||
|
);
|
||||||
|
|
||||||
|
const dsoObs = sqrObs.pipe(
|
||||||
|
map((sqr: SearchQueryResponse) => {
|
||||||
|
return sqr.objects.map((nsr: NormalizedSearchResult) =>
|
||||||
|
this.rdb.buildSingle(nsr.dspaceObject, NormalizedDSpaceObject));
|
||||||
|
}),
|
||||||
|
flatMap((input: Array<Observable<RemoteData<DSpaceObject>>>) => this.rdb.aggregate(input))
|
||||||
|
);
|
||||||
|
|
||||||
|
const payloadObs = Observable.combineLatest(sqrObs, dsoObs, (sqr: SearchQueryResponse, dsos: RemoteData<DSpaceObject[]>) => {
|
||||||
|
return sqr.objects.map((object: NormalizedSearchResult, index: number) => {
|
||||||
|
return Object.assign({}, object, {
|
||||||
|
dspaceObject: dsos.payload[index]
|
||||||
|
});
|
||||||
})
|
})
|
||||||
).subscribe((request: RestRequest) => this.requestService.configure(request));
|
});
|
||||||
return Observable.of(undefined);
|
|
||||||
|
return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfig(): Observable<RemoteData<SearchFilterConfig[]>> {
|
getConfig(): Observable<RemoteData<SearchFilterConfig[]>> {
|
||||||
|
@@ -1,18 +1,24 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { map, tap } from 'rxjs/operators';
|
||||||
|
import { NormalizedSearchResult } from '../../../+search-page/normalized-search-result.model';
|
||||||
|
import { SearchResult } from '../../../+search-page/search-result.model';
|
||||||
|
import { SearchQueryResponse } from '../../../+search-page/search-service/search-query-response.model';
|
||||||
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
|
import { hasValue, 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';
|
||||||
import { RemoteDataError } from '../../data/remote-data-error';
|
import { RemoteDataError } from '../../data/remote-data-error';
|
||||||
import { GetRequest } from '../../data/request.models';
|
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 { DSpaceObject } from '../../shared/dspace-object.model';
|
||||||
import { GenericConstructor } from '../../shared/generic-constructor';
|
import { GenericConstructor } from '../../shared/generic-constructor';
|
||||||
|
import { NormalizedDSpaceObject } from '../models/normalized-dspace-object.model';
|
||||||
import { NormalizedObjectFactory } from '../models/normalized-object-factory';
|
import { NormalizedObjectFactory } from '../models/normalized-object-factory';
|
||||||
|
|
||||||
import { CacheableObject } from '../object-cache.reducer';
|
import { CacheableObject } from '../object-cache.reducer';
|
||||||
import { ObjectCacheService } from '../object-cache.service';
|
import { ObjectCacheService } from '../object-cache.service';
|
||||||
import { DSOSuccessResponse, ErrorResponse } from '../response-cache.models';
|
import { DSOSuccessResponse, ErrorResponse, SearchSuccessResponse } from '../response-cache.models';
|
||||||
import { ResponseCacheEntry } from '../response-cache.reducer';
|
import { ResponseCacheEntry } from '../response-cache.reducer';
|
||||||
import { ResponseCacheService } from '../response-cache.service';
|
import { ResponseCacheService } from '../response-cache.service';
|
||||||
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
|
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
|
||||||
@@ -37,7 +43,7 @@ export class RemoteDataBuildService {
|
|||||||
const requestHrefObs = hrefObs.flatMap((href: string) =>
|
const requestHrefObs = hrefObs.flatMap((href: string) =>
|
||||||
this.objectCache.getRequestHrefBySelfLink(href));
|
this.objectCache.getRequestHrefBySelfLink(href));
|
||||||
|
|
||||||
const requestObs = Observable.race(
|
const requestEntryObs = Observable.race(
|
||||||
hrefObs.flatMap((href: string) => this.requestService.getByHref(href))
|
hrefObs.flatMap((href: string) => this.requestService.getByHref(href))
|
||||||
.filter((entry) => hasValue(entry)),
|
.filter((entry) => hasValue(entry)),
|
||||||
requestHrefObs.flatMap((requestHref) =>
|
requestHrefObs.flatMap((requestHref) =>
|
||||||
@@ -80,21 +86,23 @@ export class RemoteDataBuildService {
|
|||||||
})
|
})
|
||||||
.startWith(undefined)
|
.startWith(undefined)
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
return this.toRemoteDataObservable(hrefObs, requestObs, responseCacheObs, payloadObs);
|
return this.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private toRemoteDataObservable<T>(hrefObs: Observable<string>, requestObs: Observable<RequestEntry>, responseCacheObs: Observable<ResponseCacheEntry>, payloadObs: Observable<T>) {
|
toRemoteDataObservable<T>(requestEntryObs: Observable<RequestEntry>, responseCacheObs: Observable<ResponseCacheEntry>, payloadObs: Observable<T>) {
|
||||||
return Observable.combineLatest(hrefObs, requestObs, responseCacheObs.startWith(undefined), payloadObs,
|
return Observable.combineLatest(requestEntryObs, responseCacheObs.startWith(undefined), payloadObs,
|
||||||
(href: string, reqEntry: RequestEntry, resEntry: ResponseCacheEntry, payload: T) => {
|
(reqEntry: RequestEntry, resEntry: ResponseCacheEntry, payload: T) => {
|
||||||
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(resEntry) && hasValue(resEntry.response)) {
|
||||||
isSuccessful = resEntry.response.isSuccessful;
|
isSuccessful = !responsePending && resEntry.response.isSuccessful;
|
||||||
const errorMessage = isSuccessful === false ? (resEntry.response as ErrorResponse).errorMessage : undefined;
|
const errorMessage = isSuccessful === false ? (resEntry.response as ErrorResponse).errorMessage : undefined;
|
||||||
|
if (hasValue(errorMessage)) {
|
||||||
error = new RemoteDataError(resEntry.response.statusCode, errorMessage);
|
error = new RemoteDataError(resEntry.response.statusCode, errorMessage);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new RemoteData(
|
return new RemoteData(
|
||||||
requestPending,
|
requestPending,
|
||||||
@@ -114,7 +122,7 @@ export class RemoteDataBuildService {
|
|||||||
hrefObs = Observable.of(hrefObs);
|
hrefObs = Observable.of(hrefObs);
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestObs = hrefObs.flatMap((href: string) => this.requestService.getByHref(href))
|
const requestEntryObs = hrefObs.flatMap((href: string) => this.requestService.getByHref(href))
|
||||||
.filter((entry) => hasValue(entry));
|
.filter((entry) => hasValue(entry));
|
||||||
const responseCacheObs = hrefObs.flatMap((href: string) => this.responseCache.get(href))
|
const responseCacheObs = hrefObs.flatMap((href: string) => this.responseCache.get(href))
|
||||||
.filter((entry) => hasValue(entry));
|
.filter((entry) => hasValue(entry));
|
||||||
@@ -154,10 +162,10 @@ export class RemoteDataBuildService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.toRemoteDataObservable(hrefObs, requestObs, responseCacheObs, payloadObs);
|
return this.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs);
|
||||||
}
|
}
|
||||||
|
|
||||||
build<TNormalized extends CacheableObject, TDomain>(normalized: TNormalized): TDomain {
|
build<TNormalized, TDomain>(normalized: TNormalized): TDomain {
|
||||||
const links: any = {};
|
const links: any = {};
|
||||||
|
|
||||||
const relationships = getRelationships(normalized.constructor) || [];
|
const relationships = getRelationships(normalized.constructor) || [];
|
||||||
|
@@ -1,13 +1,16 @@
|
|||||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
import { autoserialize, autoserializeAs } from 'cerialize';
|
||||||
|
import { DSpaceObject } from '../../shared/dspace-object.model';
|
||||||
|
|
||||||
import { Metadatum } from '../../shared/metadatum.model';
|
import { Metadatum } from '../../shared/metadatum.model';
|
||||||
import { ResourceType } from '../../shared/resource-type';
|
import { ResourceType } from '../../shared/resource-type';
|
||||||
|
import { mapsTo } from '../builders/build-decorators';
|
||||||
import { NormalizedObject } from './normalized-object.model';
|
import { NormalizedObject } from './normalized-object.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract model class for a DSpaceObject.
|
* An model class for a DSpaceObject.
|
||||||
*/
|
*/
|
||||||
export abstract class NormalizedDSpaceObject extends NormalizedObject {
|
@mapsTo(DSpaceObject)
|
||||||
|
export class NormalizedDSpaceObject extends NormalizedObject {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The link to the rest endpoint where this object can be found
|
* The link to the rest endpoint where this object can be found
|
||||||
|
@@ -9,7 +9,7 @@ import { Observable } from 'rxjs/Observable';
|
|||||||
/**
|
/**
|
||||||
* An abstract model class for a DSpaceObject.
|
* An abstract model class for a DSpaceObject.
|
||||||
*/
|
*/
|
||||||
export abstract class DSpaceObject implements CacheableObject, ListableObject {
|
export class DSpaceObject implements CacheableObject, ListableObject {
|
||||||
|
|
||||||
self: string;
|
self: string;
|
||||||
|
|
||||||
|
@@ -1,8 +1,5 @@
|
|||||||
/**
|
|
||||||
* TODO replace with actual string enum after upgrade to TypeScript 2.4:
|
|
||||||
* https://github.com/Microsoft/TypeScript/pull/15486
|
|
||||||
*/
|
|
||||||
export enum ResourceType {
|
export enum ResourceType {
|
||||||
|
DSpaceObject = 'dspaceobject',
|
||||||
Bundle = 'bundle',
|
Bundle = 'bundle',
|
||||||
Bitstream = 'bitstream',
|
Bitstream = 'bitstream',
|
||||||
BitstreamFormat = 'bitstreamformat',
|
BitstreamFormat = 'bitstreamformat',
|
||||||
|
Reference in New Issue
Block a user