mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-09 19:13:08 +00:00
get browse endpoints from hal links
This commit is contained in:
@@ -22,6 +22,7 @@ import { Observable } from 'rxjs/Observable';
|
||||
selector: 'ds-collection-page',
|
||||
styleUrls: ['./collection-page.component.scss'],
|
||||
templateUrl: './collection-page.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class CollectionPageComponent implements OnInit, OnDestroy {
|
||||
collectionData: RemoteData<Collection>;
|
||||
@@ -37,8 +38,7 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
|
||||
private route: ActivatedRoute) {
|
||||
this.paginationConfig = new PaginationComponentOptions();
|
||||
this.paginationConfig.id = 'collection-page-pagination';
|
||||
this.paginationConfig.pageSizeOptions = [4];
|
||||
this.paginationConfig.pageSize = 4;
|
||||
this.paginationConfig.pageSize = 5;
|
||||
this.paginationConfig.currentPage = 1;
|
||||
this.sortConfig = new SortOptions();
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
@@ -13,6 +13,7 @@ import { hasValue } from '../shared/empty.util';
|
||||
selector: 'ds-community-page',
|
||||
styleUrls: ['./community-page.component.scss'],
|
||||
templateUrl: './community-page.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class CommunityPageComponent implements OnInit, OnDestroy {
|
||||
communityData: RemoteData<Community>;
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-home',
|
||||
styleUrls: ['./home.component.scss'],
|
||||
templateUrl: './home.component.html'
|
||||
templateUrl: './home.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class HomeComponent {
|
||||
|
||||
|
@@ -40,5 +40,7 @@ export class TopLevelCommunityListComponent {
|
||||
elementsPerPage: data.pageSize,
|
||||
sort: { field: data.sortField, direction: data.sortDirection }
|
||||
});
|
||||
this.cds.getScopedEndpoint('7669c72a-3f2a-451f-a3b9-9210e7a4c02f')
|
||||
.subscribe((c) => console.log('communities', c))
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Bitstream } from '../../core/shared/bitstream.model';
|
||||
selector: 'ds-item-page',
|
||||
styleUrls: ['./item-page.component.scss'],
|
||||
templateUrl: './item-page.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ItemPageComponent implements OnInit {
|
||||
|
||||
|
@@ -40,6 +40,7 @@ export function createTranslateLoader(http: HttpClient) {
|
||||
// forRoot ensures the providers are only created once
|
||||
IdlePreloadModule.forRoot(),
|
||||
RouterModule.forRoot([], {
|
||||
// enableTracing: true,
|
||||
useHash: false,
|
||||
preloadingStrategy:
|
||||
IdlePreload
|
||||
|
62
src/app/core/browse/browse.service.ts
Normal file
62
src/app/core/browse/browse.service.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
import { GLOBAL_CONFIG } from '../../../config';
|
||||
import { BrowseEndpointRequest, RestRequest } from '../data/request.models';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { BrowseSuccessResponse } from '../cache/response-cache.models';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
@Injectable()
|
||||
export class BrowseService extends HALEndpointService {
|
||||
protected linkName = 'browses';
|
||||
|
||||
private static toSearchKeyArray(metadatumKey: string): string[] {
|
||||
const keyParts = metadatumKey.split('.');
|
||||
const searchFor = [];
|
||||
searchFor.push('*');
|
||||
for (let i = 0; i < keyParts.length - 1; i++) {
|
||||
const prevParts = keyParts.slice(0, i + 1);
|
||||
const nextPart = [...prevParts, '*'].join('.');
|
||||
searchFor.push(nextPart);
|
||||
}
|
||||
searchFor.push(metadatumKey);
|
||||
return searchFor;
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig) {
|
||||
super();
|
||||
}
|
||||
|
||||
getBrowseURLFor(metadatumKey: string, linkName: string): Observable<string> {
|
||||
const searchKeyArray = BrowseService.toSearchKeyArray(metadatumKey);
|
||||
return this.getEndpoint()
|
||||
.filter((href: string) => isNotEmpty(href))
|
||||
.distinctUntilChanged()
|
||||
.map((endpointURL: string) => new BrowseEndpointRequest(endpointURL))
|
||||
.do((request: RestRequest) => {
|
||||
setTimeout(() => {
|
||||
this.requestService.configure(request);
|
||||
}, 0);
|
||||
})
|
||||
.flatMap((request: RestRequest) => this.responseCache.get(request.href)
|
||||
.map((entry: ResponseCacheEntry) => entry.response)
|
||||
.filter((response: BrowseSuccessResponse) => isNotEmpty(response) && isNotEmpty(response.browseDefinitions))
|
||||
.map((response: BrowseSuccessResponse) => response.browseDefinitions)
|
||||
.map((browseDefinitions: BrowseDefinition[]) => browseDefinitions
|
||||
.find((def: BrowseDefinition) => {
|
||||
const matchingKeys = def.metadataKeys.find((key: string) => searchKeyArray.indexOf(key) >= 0);
|
||||
return matchingKeys.length > 0
|
||||
})
|
||||
).map((def: BrowseDefinition) => def._links[linkName])
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -66,4 +66,14 @@ export abstract class NormalizedDSpaceObject extends NormalizedObject {
|
||||
@autoserialize
|
||||
owner: string;
|
||||
|
||||
/**
|
||||
* The links to all related resources returned by the rest api.
|
||||
*
|
||||
* Repeated here to make the serialization work,
|
||||
* inheritSerialization doesn't seem to work for more than one level
|
||||
*/
|
||||
@autoserialize
|
||||
_links: {
|
||||
[name: string]: string
|
||||
}
|
||||
}
|
||||
|
@@ -17,4 +17,8 @@ export abstract class NormalizedObject implements CacheableObject {
|
||||
@autoserialize
|
||||
uuid: string;
|
||||
|
||||
@autoserialize
|
||||
_links: {
|
||||
[name: string]: string
|
||||
}
|
||||
}
|
||||
|
10
src/app/core/cache/response-cache.models.ts
vendored
10
src/app/core/cache/response-cache.models.ts
vendored
@@ -1,5 +1,6 @@
|
||||
import { RequestError } from '../data/request.models';
|
||||
import { PageInfo } from '../shared/page-info.model';
|
||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
export class RestResponse {
|
||||
@@ -32,6 +33,15 @@ export class RootSuccessResponse extends RestResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export class BrowseSuccessResponse extends RestResponse {
|
||||
constructor(
|
||||
public browseDefinitions: BrowseDefinition[],
|
||||
public statusCode: string
|
||||
) {
|
||||
super(true, statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
export class ErrorResponse extends RestResponse {
|
||||
errorMessage: string;
|
||||
|
||||
|
@@ -24,6 +24,8 @@ import { HostWindowService } from '../shared/host-window.service';
|
||||
import { NativeWindowFactory, NativeWindowService } from '../shared/window.service';
|
||||
|
||||
import { ServerResponseService } from '../shared/server-response.service';
|
||||
import { BrowseService } from './browse/browse.service';
|
||||
import { BrowseResponseParsingService } from './data/browse-response-parsing.service';
|
||||
|
||||
const IMPORTS = [
|
||||
CommonModule,
|
||||
@@ -54,6 +56,8 @@ const PROVIDERS = [
|
||||
ResponseCacheService,
|
||||
RootResponseParsingService,
|
||||
ServerResponseService,
|
||||
BrowseResponseParsingService,
|
||||
BrowseService,
|
||||
{ provide: NativeWindowService, useFactory: NativeWindowFactory }
|
||||
];
|
||||
|
||||
|
28
src/app/core/data/browse-response-parsing.service.ts
Normal file
28
src/app/core/data/browse-response-parsing.service.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RestRequest } from './request.models';
|
||||
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
|
||||
import { BrowseSuccessResponse, ErrorResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
|
||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||
|
||||
@Injectable()
|
||||
export class BrowseResponseParsingService implements ResponseParsingService {
|
||||
|
||||
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
|
||||
if (isNotEmpty(data.payload) && isNotEmpty(data.payload._embedded)
|
||||
&& Array.isArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]])) {
|
||||
const serializer = new DSpaceRESTv2Serializer(BrowseDefinition);
|
||||
const browseDefinitions = serializer.deserializeArray(data.payload._embedded[Object.keys(data.payload._embedded)[0]]);
|
||||
return new BrowseSuccessResponse(browseDefinitions, data.statusCode);
|
||||
} else {
|
||||
return new ErrorResponse(
|
||||
Object.assign(
|
||||
new Error('Unexpected response from browse endpoint'),
|
||||
{ statusText: data.statusCode }
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,20 +9,54 @@ import { CoreState } from '../core.reducers';
|
||||
import { RequestService } from './request.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { CommunityDataService } from './community-data.service';
|
||||
import { FindByIDRequest } from './request.models';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { NormalizedCommunity } from '../cache/models/normalized-community.model';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
|
||||
@Injectable()
|
||||
export class CollectionDataService extends DataService<NormalizedCollection, Collection> {
|
||||
protected linkName = 'collections';
|
||||
protected browseEndpoint = '/discover/browses/dateissued/collections';
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
@Inject(GLOBAL_CONFIG) EnvConfig: GlobalConfig
|
||||
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||
private cds: CommunityDataService,
|
||||
protected objectCache: ObjectCacheService
|
||||
) {
|
||||
super(NormalizedCollection, EnvConfig);
|
||||
super(NormalizedCollection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scoped endpoint URL by fetching the object with
|
||||
* the given scopeID and returning its HAL link with this
|
||||
* data-service's linkName
|
||||
*
|
||||
* @param {string} scopeID
|
||||
* the id of the scope object
|
||||
* @return { Observable<string> }
|
||||
* an Observable<string> containing the scoped URL
|
||||
*/
|
||||
public getScopedEndpoint(scopeID: string): Observable<string> {
|
||||
this.cds.getEndpoint()
|
||||
.map((endpoint: string) => this.cds.getFindByIDHref(endpoint, scopeID))
|
||||
.filter((href: string) => isNotEmpty(href))
|
||||
.take(1)
|
||||
.subscribe((href: string) => {
|
||||
const request = new FindByIDRequest(href, scopeID);
|
||||
setTimeout(() => {
|
||||
this.requestService.configure(request);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
return this.objectCache.getByUUID(scopeID, NormalizedCommunity)
|
||||
.map((nc: NormalizedCommunity) => nc._links[this.linkName])
|
||||
.filter((href) => isNotEmpty(href))
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
}
|
||||
|
@@ -10,20 +10,52 @@ import { CoreState } from '../core.reducers';
|
||||
import { RequestService } from './request.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { FindByIDRequest } from './request.models';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
|
||||
@Injectable()
|
||||
export class CommunityDataService extends DataService<NormalizedCommunity, Community> {
|
||||
protected linkName = 'communities';
|
||||
protected browseEndpoint = '/discover/browses/dateissued/communities';
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
@Inject(GLOBAL_CONFIG) EnvConfig: GlobalConfig
|
||||
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||
protected objectCache: ObjectCacheService
|
||||
) {
|
||||
super(NormalizedCommunity, EnvConfig);
|
||||
super(NormalizedCommunity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scoped endpoint URL by fetching the object with
|
||||
* the given scopeID and returning its HAL link with this
|
||||
* data-service's linkName
|
||||
*
|
||||
* @param {string} scopeID
|
||||
* the id of the scope object
|
||||
* @return { Observable<string> }
|
||||
* an Observable<string> containing the scoped URL
|
||||
*/
|
||||
public getScopedEndpoint(scopeID: string): Observable<string> {
|
||||
this.getEndpoint()
|
||||
.map((endpoint: string) => this.getFindByIDHref(endpoint, scopeID))
|
||||
.filter((href: string) => isNotEmpty(href))
|
||||
.take(1)
|
||||
.subscribe((href: string) => {
|
||||
const request = new FindByIDRequest(href, scopeID);
|
||||
setTimeout(() => {
|
||||
this.requestService.configure(request);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
return this.objectCache.getByUUID(scopeID, NormalizedCommunity)
|
||||
.map((nc: NormalizedCommunity) => nc._links[this.linkName])
|
||||
.filter((href) => isNotEmpty(href))
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,71 +1,41 @@
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||
import { RemoteData } from './remote-data';
|
||||
import {
|
||||
FindAllOptions, FindAllRequest, FindByIDRequest, RestRequest,
|
||||
RootEndpointRequest
|
||||
} from './request.models';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { RequestService } from './request.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { GenericConstructor } from '../shared/generic-constructor';
|
||||
import { GlobalConfig } from '../../../config';
|
||||
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { EndpointMap, RootSuccessResponse } from '../cache/response-cache.models';
|
||||
import { GlobalConfig } from '../../../config';
|
||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { GenericConstructor } from '../shared/generic-constructor';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RemoteData } from './remote-data';
|
||||
import { FindAllOptions, FindAllRequest, FindByIDRequest, RestRequest } from './request.models';
|
||||
import { RequestService } from './request.service';
|
||||
|
||||
export abstract class DataService<TNormalized extends CacheableObject, TDomain> {
|
||||
export abstract class DataService<TNormalized extends CacheableObject, TDomain> extends HALEndpointService {
|
||||
protected abstract responseCache: ResponseCacheService;
|
||||
protected abstract requestService: RequestService;
|
||||
protected abstract rdbService: RemoteDataBuildService;
|
||||
protected abstract store: Store<CoreState>;
|
||||
protected abstract linkName: string;
|
||||
protected abstract browseEndpoint: string;
|
||||
protected abstract EnvConfig: GlobalConfig
|
||||
|
||||
constructor(
|
||||
private normalizedResourceType: GenericConstructor<TNormalized>,
|
||||
protected EnvConfig: GlobalConfig
|
||||
) {
|
||||
|
||||
super();
|
||||
}
|
||||
|
||||
private getEndpointMap(): Observable<EndpointMap> {
|
||||
const request = new RootEndpointRequest(this.EnvConfig);
|
||||
this.requestService.configure(request);
|
||||
return this.responseCache.get(request.href)
|
||||
.map((entry: ResponseCacheEntry) => entry.response)
|
||||
.filter((response: RootSuccessResponse) => isNotEmpty(response) && isNotEmpty(response.endpointMap))
|
||||
.map((response: RootSuccessResponse) => response.endpointMap)
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
public abstract getScopedEndpoint(scope: string): Observable<string>
|
||||
|
||||
public getEndpoint(): Observable<string> {
|
||||
const request = new RootEndpointRequest(this.EnvConfig);
|
||||
this.requestService.configure(request);
|
||||
return this.getEndpointMap()
|
||||
.map((map: EndpointMap) => map[this.linkName])
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
|
||||
public isEnabledOnRestApi(): Observable<boolean> {
|
||||
return this.getEndpointMap()
|
||||
.map((map: EndpointMap) => isNotEmpty(map[this.linkName]))
|
||||
.startWith(undefined)
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
|
||||
protected getFindAllHref(endpoint, options: FindAllOptions = {}): string {
|
||||
protected getFindAllHref(endpoint, options: FindAllOptions = {}): Observable<string> {
|
||||
let result;
|
||||
const args = [];
|
||||
|
||||
if (hasValue(options.scopeID)) {
|
||||
result = new RESTURLCombiner(this.EnvConfig, this.browseEndpoint).toString();
|
||||
args.push(`scope=${options.scopeID}`);
|
||||
result = this.getScopedEndpoint(options.scopeID);
|
||||
} else {
|
||||
result = endpoint;
|
||||
result = Observable.of(endpoint);
|
||||
}
|
||||
|
||||
if (hasValue(options.currentPage) && typeof options.currentPage === 'number') {
|
||||
@@ -86,25 +56,28 @@ export abstract class DataService<TNormalized extends CacheableObject, TDomain>
|
||||
}
|
||||
|
||||
if (isNotEmpty(args)) {
|
||||
result = `${result}?${args.join('&')}`;
|
||||
return result.map((href: string) => `${href}?${args.join('&')}`);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
findAll(options: FindAllOptions = {}): RemoteData<TDomain[]> {
|
||||
const hrefObs = this.getEndpoint()
|
||||
.map((endpoint: string) => this.getFindAllHref(endpoint, options));
|
||||
.flatMap((endpoint: string) => this.getFindAllHref(endpoint, options));
|
||||
|
||||
hrefObs
|
||||
.subscribe((href: string) => {
|
||||
const request = new FindAllRequest(href, options);
|
||||
this.requestService.configure(request);
|
||||
setTimeout(() => {
|
||||
this.requestService.configure(request);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
return this.rdbService.buildList<TNormalized, TDomain>(hrefObs, this.normalizedResourceType);
|
||||
}
|
||||
|
||||
protected getFindByIDHref(endpoint, resourceID): string {
|
||||
getFindByIDHref(endpoint, resourceID): string {
|
||||
return `${endpoint}/${resourceID}`;
|
||||
}
|
||||
|
||||
@@ -115,14 +88,18 @@ export abstract class DataService<TNormalized extends CacheableObject, TDomain>
|
||||
hrefObs
|
||||
.subscribe((href: string) => {
|
||||
const request = new FindByIDRequest(href, id);
|
||||
this.requestService.configure(request);
|
||||
setTimeout(() => {
|
||||
this.requestService.configure(request);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
return this.rdbService.buildSingle<TNormalized, TDomain>(hrefObs, this.normalizedResourceType);
|
||||
}
|
||||
|
||||
findByHref(href: string): RemoteData<TDomain> {
|
||||
this.requestService.configure(new RestRequest(href));
|
||||
setTimeout(() => {
|
||||
this.requestService.configure(new RestRequest(href));
|
||||
}, 0);
|
||||
return this.rdbService.buildSingle<TNormalized, TDomain>(href, this.normalizedResourceType);
|
||||
// return this.rdbService.buildSingle(href));
|
||||
}
|
||||
|
@@ -10,19 +10,29 @@ import { NormalizedItem } from '../cache/models/normalized-item.model';
|
||||
import { RequestService } from './request.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../config';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { BrowseService } from '../browse/browse.service';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
|
||||
@Injectable()
|
||||
export class ItemDataService extends DataService<NormalizedItem, Item> {
|
||||
protected linkName = 'items';
|
||||
protected browseEndpoint = '/discover/browses/dateissued/items';
|
||||
|
||||
constructor(
|
||||
protected responseCache: ResponseCacheService,
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
@Inject(GLOBAL_CONFIG) EnvConfig: GlobalConfig
|
||||
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
|
||||
private bs: BrowseService
|
||||
) {
|
||||
super(NormalizedItem, EnvConfig);
|
||||
super(NormalizedItem);
|
||||
}
|
||||
|
||||
public getScopedEndpoint(scopeID: string): Observable<string> {
|
||||
return this.bs.getBrowseURLFor('dc.date.issued', this.linkName)
|
||||
.filter((href) => isNotEmpty(href))
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
|
||||
import { DSOResponseParsingService } from './dso-response-parsing.service';
|
||||
import { ResponseParsingService } from './parsing.service';
|
||||
import { RootResponseParsingService } from './root-response-parsing.service';
|
||||
import { BrowseResponseParsingService } from './browse-response-parsing.service';
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
export class RestRequest {
|
||||
@@ -53,6 +54,16 @@ export class RootEndpointRequest extends RestRequest {
|
||||
}
|
||||
}
|
||||
|
||||
export class BrowseEndpointRequest extends RestRequest {
|
||||
constructor(href: string) {
|
||||
super(href);
|
||||
}
|
||||
|
||||
getResponseParser(): GenericConstructor<ResponseParsingService> {
|
||||
return BrowseResponseParsingService;
|
||||
}
|
||||
}
|
||||
|
||||
export class RequestError extends Error {
|
||||
statusText: string;
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ import { Observable } from 'rxjs/Observable';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { DSOSuccessResponse } from '../cache/response-cache.models';
|
||||
import { DSOSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
@@ -47,16 +47,20 @@ export class RequestService {
|
||||
|
||||
configure<T extends CacheableObject>(request: RestRequest): void {
|
||||
let isCached = this.objectCache.hasBySelfLink(request.href);
|
||||
|
||||
// console.log('request.href', request.href);
|
||||
if (!isCached && this.responseCache.has(request.href)) {
|
||||
const [dsoSuccessResponse, otherSuccessResponse] = this.responseCache.get(request.href)
|
||||
const [successResponse, errorResponse] = this.responseCache.get(request.href)
|
||||
.take(1)
|
||||
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
|
||||
.map((entry: ResponseCacheEntry) => entry.response)
|
||||
.share()
|
||||
.partition((response: RestResponse) => response.isSuccessful);
|
||||
|
||||
const [dsoSuccessResponse, otherSuccessResponse] = successResponse
|
||||
.share()
|
||||
.partition((response: DSOSuccessResponse) => hasValue(response.resourceSelfLinks));
|
||||
|
||||
Observable.merge(
|
||||
errorResponse.map(() => true), // TODO add a configurable number of retries in case of an error.
|
||||
otherSuccessResponse.map(() => true),
|
||||
dsoSuccessResponse // a DSOSuccessResponse should only be considered cached if all its resources are cached
|
||||
.map((response: DSOSuccessResponse) => response.resourceSelfLinks)
|
||||
|
@@ -19,12 +19,7 @@ export class RootResponseParsingService implements ResponseParsingService {
|
||||
if (isNotEmpty(data.payload) && isNotEmpty(data.payload._links)) {
|
||||
const links = data.payload._links;
|
||||
for (const link of Object.keys(links)) {
|
||||
let href = links[link].href;
|
||||
// TODO temporary workaround as these endpoint paths are relative, but should be absolute
|
||||
if (isNotEmpty(href) && !href.startsWith('http')) {
|
||||
href = new RESTURLCombiner(this.EnvConfig, href.substring(this.EnvConfig.rest.nameSpace.length)).toString();
|
||||
}
|
||||
links[link] = href;
|
||||
links[link] = links[link].href;
|
||||
}
|
||||
return new RootSuccessResponse(links, data.statusCode);
|
||||
} else {
|
||||
|
24
src/app/core/shared/browse-definition.model.ts
Normal file
24
src/app/core/shared/browse-definition.model.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { autoserialize, autoserializeAs } from 'cerialize';
|
||||
import { SortOption } from './sort-option.model';
|
||||
|
||||
export class BrowseDefinition {
|
||||
@autoserialize
|
||||
metadataBrowse: boolean;
|
||||
|
||||
@autoserialize
|
||||
sortOptions: SortOption[];
|
||||
|
||||
@autoserializeAs('order')
|
||||
defaultSortOrder: string;
|
||||
|
||||
@autoserialize
|
||||
type: string;
|
||||
|
||||
@autoserializeAs('metadata')
|
||||
metadataKeys: string[];
|
||||
|
||||
@autoserialize
|
||||
_links: {
|
||||
[name: string]: string
|
||||
}
|
||||
}
|
46
src/app/core/shared/hal-endpoint.service.ts
Normal file
46
src/app/core/shared/hal-endpoint.service.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||
import { GlobalConfig } from '../../../config/global-config.interface';
|
||||
import { EndpointMap, RootSuccessResponse } from '../cache/response-cache.models';
|
||||
import { RootEndpointRequest } from '../data/request.models';
|
||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
|
||||
export abstract class HALEndpointService {
|
||||
protected abstract responseCache: ResponseCacheService;
|
||||
protected abstract requestService: RequestService;
|
||||
protected abstract linkName: string;
|
||||
protected abstract EnvConfig: GlobalConfig;
|
||||
|
||||
protected getEndpointMap(): Observable<EndpointMap> {
|
||||
const request = new RootEndpointRequest(this.EnvConfig);
|
||||
setTimeout(() => {
|
||||
this.requestService.configure(request);
|
||||
}, 0);
|
||||
return this.responseCache.get(request.href)
|
||||
.map((entry: ResponseCacheEntry) => entry.response)
|
||||
.filter((response: RootSuccessResponse) => isNotEmpty(response) && isNotEmpty(response.endpointMap))
|
||||
.map((response: RootSuccessResponse) => response.endpointMap)
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
|
||||
public getEndpoint(): Observable<string> {
|
||||
return this.getEndpointMap()
|
||||
.do((map: EndpointMap) => {
|
||||
if (!this.linkName) {
|
||||
console.log('map', this)
|
||||
}
|
||||
})
|
||||
.map((map: EndpointMap) => map[this.linkName])
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
|
||||
public isEnabledOnRestApi(): Observable<boolean> {
|
||||
return this.getEndpointMap()
|
||||
.map((map: EndpointMap) => isNotEmpty(map[this.linkName]))
|
||||
.startWith(undefined)
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
|
||||
}
|
9
src/app/core/shared/sort-option.model.ts
Normal file
9
src/app/core/shared/sort-option.model.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { autoserialize } from 'cerialize';
|
||||
|
||||
export class SortOption {
|
||||
@autoserialize
|
||||
name: string;
|
||||
|
||||
@autoserialize
|
||||
metadata: string;
|
||||
}
|
Reference in New Issue
Block a user