mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
followlinks
This commit is contained in:
@@ -6,6 +6,7 @@ import { CollectionDataService } from '../core/data/collection-data.service';
|
||||
import { RemoteData } from '../core/data/remote-data';
|
||||
import { find } from 'rxjs/operators';
|
||||
import { hasValue } from '../shared/empty.util';
|
||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||
|
||||
/**
|
||||
* This class represents a resolver that requests a specific collection before the route is activated
|
||||
@@ -23,7 +24,7 @@ export class CollectionPageResolver implements Resolve<RemoteData<Collection>> {
|
||||
* or an error if something went wrong
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Collection>> {
|
||||
return this.collectionService.findById(route.params.id).pipe(
|
||||
return this.collectionService.findById(route.params.id, followLink('logo')).pipe(
|
||||
find((RD) => hasValue(RD.error) || RD.hasSucceeded),
|
||||
);
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import { Community } from '../core/shared/community.model';
|
||||
import { CommunityDataService } from '../core/data/community-data.service';
|
||||
import { find } from 'rxjs/operators';
|
||||
import { hasValue } from '../shared/empty.util';
|
||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||
|
||||
/**
|
||||
* This class represents a resolver that requests a specific community before the route is activated
|
||||
@@ -23,7 +24,12 @@ export class CommunityPageResolver implements Resolve<RemoteData<Community>> {
|
||||
* or an error if something went wrong
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Community>> {
|
||||
return this.communityService.findById(route.params.id).pipe(
|
||||
return this.communityService.findById(
|
||||
route.params.id,
|
||||
followLink('logo'),
|
||||
followLink('subcommunities'),
|
||||
followLink('collections')
|
||||
).pipe(
|
||||
find((RD) => hasValue(RD.error) || RD.hasSucceeded)
|
||||
);
|
||||
}
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { Component, Injector, Input, OnInit } from '@angular/core';
|
||||
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
import { getBitstreamBuilder } from '../../../../core/cache/builders/bitstream-builder';
|
||||
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||
|
||||
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
|
||||
import { followLink } from '../../../../shared/utils/follow-link-config.model';
|
||||
import { FileSectionComponent } from '../../../simple/field-components/file-section/file-section.component';
|
||||
|
||||
/**
|
||||
@@ -28,8 +28,7 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
|
||||
bitstreams$: Observable<Bitstream[]>;
|
||||
|
||||
constructor(
|
||||
bitstreamDataService: BitstreamDataService,
|
||||
private parentInjector: Injector
|
||||
bitstreamDataService: BitstreamDataService
|
||||
) {
|
||||
super(bitstreamDataService);
|
||||
}
|
||||
@@ -40,11 +39,21 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
|
||||
|
||||
initialize(): void {
|
||||
// TODO pagination
|
||||
const originals$ = this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'ORIGINAL', { elementsPerPage: Number.MAX_SAFE_INTEGER }).pipe(
|
||||
const originals$ = this.bitstreamDataService.findAllByItemAndBundleName(
|
||||
this.item,
|
||||
'ORIGINAL',
|
||||
{ elementsPerPage: Number.MAX_SAFE_INTEGER },
|
||||
followLink( 'format')
|
||||
).pipe(
|
||||
getFirstSucceededRemoteListPayload(),
|
||||
startWith([])
|
||||
);
|
||||
const licenses$ = this.bitstreamDataService.findAllByItemAndBundleName(this.item, 'LICENSE', { elementsPerPage: Number.MAX_SAFE_INTEGER }).pipe(
|
||||
const licenses$ = this.bitstreamDataService.findAllByItemAndBundleName(
|
||||
this.item,
|
||||
'LICENSE',
|
||||
{ elementsPerPage: Number.MAX_SAFE_INTEGER },
|
||||
followLink( 'format')
|
||||
).pipe(
|
||||
getFirstSucceededRemoteListPayload(),
|
||||
startWith([])
|
||||
);
|
||||
@@ -53,10 +62,8 @@ export class FullFileSectionComponent extends FileSectionComponent implements On
|
||||
map((files: Bitstream[]) =>
|
||||
files.map(
|
||||
(original) => {
|
||||
return getBitstreamBuilder(this.parentInjector, original)
|
||||
.loadThumbnail(this.item)
|
||||
.loadBitstreamFormat()
|
||||
.build();
|
||||
original.thumbnail = this.bitstreamDataService.getMatchingThumbnail(this.item, original);
|
||||
return original;
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@@ -6,6 +6,7 @@ import { ItemDataService } from '../core/data/item-data.service';
|
||||
import { Item } from '../core/shared/item.model';
|
||||
import { hasValue } from '../shared/empty.util';
|
||||
import { find } from 'rxjs/operators';
|
||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||
|
||||
/**
|
||||
* This class represents a resolver that requests a specific item before the route is activated
|
||||
@@ -23,9 +24,12 @@ export class ItemPageResolver implements Resolve<RemoteData<Item>> {
|
||||
* or an error if something went wrong
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<Item>> {
|
||||
return this.itemService.findById(route.params.id)
|
||||
.pipe(
|
||||
find((RD) => hasValue(RD.error) || RD.hasSucceeded),
|
||||
);
|
||||
return this.itemService.findById(route.params.id,
|
||||
followLink('owningCollection'),
|
||||
followLink('bundles'),
|
||||
followLink('relationships')
|
||||
).pipe(
|
||||
find((RD) => hasValue(RD.error) || RD.hasSucceeded),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ describe('LookupGuard', () => {
|
||||
guard = new LookupGuard(dsoService);
|
||||
});
|
||||
|
||||
it('should call findById with handle params', () => {
|
||||
it('should call findByIdAndIDType with handle params', () => {
|
||||
const scopedRoute = {
|
||||
params: {
|
||||
id: '1234',
|
||||
@@ -25,7 +25,7 @@ describe('LookupGuard', () => {
|
||||
expect(dsoService.findById).toHaveBeenCalledWith('123456789/1234', IdentifierType.HANDLE)
|
||||
});
|
||||
|
||||
it('should call findById with handle params', () => {
|
||||
it('should call findByIdAndIDType with handle params', () => {
|
||||
const scopedRoute = {
|
||||
params: {
|
||||
id: '123456789%2F1234',
|
||||
@@ -36,7 +36,7 @@ describe('LookupGuard', () => {
|
||||
expect(dsoService.findById).toHaveBeenCalledWith('123456789%2F1234', IdentifierType.HANDLE)
|
||||
});
|
||||
|
||||
it('should call findById with UUID params', () => {
|
||||
it('should call findByIdAndIDType with UUID params', () => {
|
||||
const scopedRoute = {
|
||||
params: {
|
||||
id: '34cfed7c-f597-49ef-9cbe-ea351f0023c2',
|
||||
|
@@ -20,7 +20,7 @@ export class LookupGuard implements CanActivate {
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
const params = this.getLookupParams(route);
|
||||
return this.dsoService.findById(params.id, params.type).pipe(
|
||||
return this.dsoService.findByIdAndIDType(params.id, params.type).pipe(
|
||||
map((response: RemoteData<FindByIDRequest>) => response.hasFailed)
|
||||
);
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
import { AuthError } from './auth-error.model';
|
||||
import { AuthTokenInfo } from './auth-token-info.model';
|
||||
import { EPerson } from '../../eperson/models/eperson.model';
|
||||
@@ -51,4 +52,8 @@ export class AuthStatus implements CacheableObject {
|
||||
* The self link of this auth status' REST object
|
||||
*/
|
||||
self: string;
|
||||
|
||||
_links: {
|
||||
self: HALLink;
|
||||
}
|
||||
}
|
||||
|
60
src/app/core/cache/builders/bitstream-builder.ts
vendored
60
src/app/core/cache/builders/bitstream-builder.ts
vendored
@@ -1,60 +0,0 @@
|
||||
import { Injector } from '@angular/core';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { BitstreamDataService } from '../../data/bitstream-data.service';
|
||||
import { BitstreamFormatDataService } from '../../data/bitstream-format-data.service';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { BitstreamFormat } from '../../shared/bitstream-format.model';
|
||||
import { Bitstream } from '../../shared/bitstream.model';
|
||||
import { Item } from '../../shared/item.model';
|
||||
|
||||
export const getBitstreamBuilder = (parentInjector: Injector, bitstream: Bitstream) => {
|
||||
const injector = Injector.create({
|
||||
providers:[
|
||||
{
|
||||
provide: BitstreamBuilder,
|
||||
useClass: BitstreamBuilder,
|
||||
deps:[
|
||||
BitstreamDataService,
|
||||
BitstreamFormatDataService,
|
||||
]
|
||||
}
|
||||
],
|
||||
parent: parentInjector
|
||||
});
|
||||
return injector.get(BitstreamBuilder).initWithBitstream(bitstream);
|
||||
};
|
||||
|
||||
export class BitstreamBuilder {
|
||||
private bitstream: Bitstream;
|
||||
private thumbnail: Observable<RemoteData<Bitstream>>;
|
||||
private format: Observable<RemoteData<BitstreamFormat>>;
|
||||
|
||||
constructor(
|
||||
protected bitstreamDataService: BitstreamDataService,
|
||||
protected bitstreamFormatDataService: BitstreamFormatDataService
|
||||
) {
|
||||
}
|
||||
|
||||
initWithBitstream(bitstream: Bitstream): BitstreamBuilder {
|
||||
this.bitstream = bitstream;
|
||||
return this;
|
||||
}
|
||||
|
||||
loadThumbnail(item: Item): BitstreamBuilder {
|
||||
this.thumbnail = this.bitstreamDataService.getMatchingThumbnail(item, this.bitstream);
|
||||
return this;
|
||||
}
|
||||
|
||||
loadBitstreamFormat(): BitstreamBuilder {
|
||||
this.format = this.bitstreamFormatDataService.findByBitstream(this.bitstream);
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): Bitstream {
|
||||
const bitstream = this.bitstream;
|
||||
bitstream.thumbnail = this.thumbnail;
|
||||
bitstream.format = this.format;
|
||||
return bitstream;
|
||||
}
|
||||
|
||||
}
|
99
src/app/core/cache/builders/build-decorators.ts
vendored
99
src/app/core/cache/builders/build-decorators.ts
vendored
@@ -1,14 +1,22 @@
|
||||
import 'reflect-metadata';
|
||||
import { hasNoValue, hasValue } from '../../../shared/empty.util';
|
||||
import { DataService } from '../../data/data.service';
|
||||
import { PaginatedList } from '../../data/paginated-list';
|
||||
|
||||
import { GenericConstructor } from '../../shared/generic-constructor';
|
||||
import { HALResource } from '../../shared/hal-resource.model';
|
||||
import { CacheableObject, TypedObject } from '../object-cache.reducer';
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
|
||||
const mapsToMetadataKey = Symbol('mapsTo');
|
||||
const relationshipKey = Symbol('relationship');
|
||||
const resolvedLinkKey = Symbol('resolvedLink');
|
||||
|
||||
const relationshipMap = new Map();
|
||||
const resolvedLinkMap = new Map();
|
||||
const typeMap = new Map();
|
||||
const dataServiceMap = new Map();
|
||||
const linkMap = new Map();
|
||||
|
||||
/**
|
||||
* Decorator function to map a normalized class to it's not-normalized counter part class
|
||||
@@ -79,3 +87,94 @@ export function getRelationMetadata(target: any, propertyKey: string) {
|
||||
export function getRelationships(target: any) {
|
||||
return relationshipMap.get(target);
|
||||
}
|
||||
|
||||
export function dataService<T extends CacheableObject>(domainModelConstructor: GenericConstructor<T>): any {
|
||||
return (target: any) => {
|
||||
if (hasNoValue(domainModelConstructor)) {
|
||||
throw new Error(`Invalid @dataService annotation on ${target}, domainModelConstructor needs to be defined`);
|
||||
}
|
||||
const existingDataservice = dataServiceMap.get(domainModelConstructor);
|
||||
|
||||
if (hasValue(existingDataservice)) {
|
||||
throw new Error(`Multiple dataservices for ${domainModelConstructor}: ${existingDataservice} and ${target}`);
|
||||
}
|
||||
|
||||
dataServiceMap.set(domainModelConstructor, target);
|
||||
};
|
||||
}
|
||||
|
||||
export function getDataServiceFor<T extends CacheableObject>(domainModelConstructor: GenericConstructor<T>) {
|
||||
return dataServiceMap.get(domainModelConstructor);
|
||||
}
|
||||
|
||||
export function resolvedLink<T extends DataService<any>, K extends keyof T>(provider: GenericConstructor<T>, methodName?: K, ...params: any[]): any {
|
||||
return function r(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
if (!target || !propertyKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
const metaDataList: string[] = resolvedLinkMap.get(target.constructor) || [];
|
||||
if (metaDataList.indexOf(propertyKey) === -1) {
|
||||
metaDataList.push(propertyKey);
|
||||
}
|
||||
resolvedLinkMap.set(target.constructor, metaDataList);
|
||||
return Reflect.metadata(resolvedLinkKey, {
|
||||
provider,
|
||||
methodName,
|
||||
params
|
||||
}).apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
export function getResolvedLinkMetadata(target: any, propertyKey: string) {
|
||||
return Reflect.getMetadata(resolvedLinkKey, target, propertyKey);
|
||||
}
|
||||
|
||||
export function getResolvedLinks(target: any) {
|
||||
return resolvedLinkMap.get(target);
|
||||
}
|
||||
|
||||
export class LinkDefinition<T extends HALResource> {
|
||||
targetConstructor: GenericConstructor<CacheableObject>;
|
||||
isList = false;
|
||||
linkName?: keyof T['_links'];
|
||||
}
|
||||
|
||||
export const link = <T extends HALResource>(
|
||||
targetConstructor: GenericConstructor<HALResource>,
|
||||
isList = false,
|
||||
linkName?: keyof T['_links'],
|
||||
) => {
|
||||
return (target: T, key: string) => {
|
||||
let targetMap = linkMap.get(target.constructor);
|
||||
|
||||
if (hasNoValue(targetMap)) {
|
||||
targetMap = new Map<keyof T['_links'],LinkDefinition<T>>();
|
||||
}
|
||||
|
||||
if (hasNoValue(linkName)) {
|
||||
linkName = key;
|
||||
}
|
||||
|
||||
targetMap.set(key, {
|
||||
targetConstructor,
|
||||
isList,
|
||||
linkName
|
||||
});
|
||||
|
||||
linkMap.set(target.constructor, targetMap);
|
||||
}
|
||||
};
|
||||
|
||||
export const getLinks = <T extends HALResource>(source: GenericConstructor<T>): Map<keyof T['_links'], LinkDefinition<T>> => {
|
||||
return linkMap.get(source);
|
||||
};
|
||||
|
||||
export const getLink = <T extends HALResource>(source: GenericConstructor<T>, linkName: keyof T['_links']): LinkDefinition<T> => {
|
||||
const sourceMap = linkMap.get(source);
|
||||
if (hasValue(sourceMap)) {
|
||||
return sourceMap.get(linkName);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
74
src/app/core/cache/builders/collection-builder.ts
vendored
Normal file
74
src/app/core/cache/builders/collection-builder.ts
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Injector } from '@angular/core';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { BitstreamDataService } from '../../data/bitstream-data.service';
|
||||
import { CollectionDataService } from '../../data/collection-data.service';
|
||||
import { PaginatedList } from '../../data/paginated-list';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { ResourcePolicyService } from '../../data/resource-policy.service';
|
||||
import { Bitstream } from '../../shared/bitstream.model';
|
||||
import { Collection } from '../../shared/collection.model';
|
||||
import { Item } from '../../shared/item.model';
|
||||
import { ResourcePolicy } from '../../shared/resource-policy.model';
|
||||
|
||||
export const getCollectionBuilder = (parentInjector: Injector, collection: Collection) => {
|
||||
const injector = Injector.create({
|
||||
providers:[
|
||||
{
|
||||
provide: CollectionBuilder,
|
||||
useClass: CollectionBuilder,
|
||||
deps:[
|
||||
CollectionDataService
|
||||
]
|
||||
}
|
||||
],
|
||||
parent: parentInjector
|
||||
});
|
||||
return injector.get(CollectionBuilder).initWithCollection(collection);
|
||||
};
|
||||
|
||||
export class CollectionBuilder {
|
||||
private collection: Collection;
|
||||
private logo: Observable<RemoteData<Bitstream>>;
|
||||
// private license: Observable<RemoteData<License>>;
|
||||
private defaultAccessConditions: Observable<RemoteData<PaginatedList<ResourcePolicy>>>;
|
||||
|
||||
constructor(
|
||||
protected collectionDataService: CollectionDataService,
|
||||
protected bitstreamDataService: BitstreamDataService,
|
||||
protected resourcePolicyService: ResourcePolicyService,
|
||||
) {
|
||||
}
|
||||
|
||||
initWithCollection(collection: Collection): CollectionBuilder {
|
||||
this.collection = collection;
|
||||
return this;
|
||||
}
|
||||
|
||||
loadLogo(item: Item): CollectionBuilder {
|
||||
// this.logo = this.bitstreamDataService.getLogoFor(this.collection);
|
||||
return this;
|
||||
}
|
||||
|
||||
loadDefaultAccessConditions(item: Item): CollectionBuilder {
|
||||
this.defaultAccessConditions = this.resourcePolicyService.getDefaultAccessConditionsFor(this.collection);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* As far as I can tell, the rest api doesn't support licenses yet.
|
||||
* So I'm keeping this commented out
|
||||
*/
|
||||
// loadLicense(): CollectionBuilder {
|
||||
// this.license = this.bitstreamDataService.getLicenseFor(this.collection);
|
||||
// return this;
|
||||
// }
|
||||
|
||||
build(): Collection {
|
||||
const collection = this.collection;
|
||||
collection.logo = this.logo;
|
||||
// collection.license = this.license;
|
||||
collection.defaultAccessConditions = this.defaultAccessConditions;
|
||||
return collection;
|
||||
}
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
|
||||
import {
|
||||
combineLatest as observableCombineLatest,
|
||||
@@ -16,16 +16,24 @@ import {
|
||||
isNotEmpty,
|
||||
isNotUndefined
|
||||
} from '../../../shared/empty.util';
|
||||
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
|
||||
import { PaginatedList } from '../../data/paginated-list';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { RemoteDataError } from '../../data/remote-data-error';
|
||||
import { GetRequest } from '../../data/request.models';
|
||||
import { RequestEntry } from '../../data/request.reducer';
|
||||
import { RequestService } from '../../data/request.service';
|
||||
import { HALResource } from '../../shared/hal-resource.model';
|
||||
import { NormalizedObject } from '../models/normalized-object.model';
|
||||
import { ObjectCacheService } from '../object-cache.service';
|
||||
import { DSOSuccessResponse, ErrorResponse } from '../response.models';
|
||||
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
|
||||
import {
|
||||
getDataServiceFor, getLink,
|
||||
getLinks,
|
||||
getMapsTo,
|
||||
getRelationMetadata,
|
||||
getRelationships
|
||||
} from './build-decorators';
|
||||
import { PageInfo } from '../../shared/page-info.model';
|
||||
import {
|
||||
filterSuccessfulResponses,
|
||||
@@ -39,10 +47,11 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils
|
||||
@Injectable()
|
||||
export class RemoteDataBuildService {
|
||||
constructor(protected objectCache: ObjectCacheService,
|
||||
private parentInjector: Injector,
|
||||
protected requestService: RequestService) {
|
||||
}
|
||||
|
||||
buildSingle<T extends CacheableObject>(href$: string | Observable<string>): Observable<RemoteData<T>> {
|
||||
buildSingle<T extends CacheableObject>(href$: string | Observable<string>, ...linksToFollow: Array<FollowLinkConfig<T>>): Observable<RemoteData<T>> {
|
||||
if (typeof href$ === 'string') {
|
||||
href$ = observableOf(href$);
|
||||
}
|
||||
@@ -83,7 +92,7 @@ export class RemoteDataBuildService {
|
||||
}),
|
||||
hasValueOperator(),
|
||||
map((normalized: NormalizedObject<T>) => {
|
||||
return this.build<T>(normalized);
|
||||
return this.build<T>(normalized, ...linksToFollow);
|
||||
}),
|
||||
startWith(undefined),
|
||||
distinctUntilChanged()
|
||||
@@ -120,7 +129,7 @@ export class RemoteDataBuildService {
|
||||
);
|
||||
}
|
||||
|
||||
buildList<T extends CacheableObject>(href$: string | Observable<string>): Observable<RemoteData<PaginatedList<T>>> {
|
||||
buildList<T extends CacheableObject>(href$: string | Observable<string>, ...linksToFollow: Array<FollowLinkConfig<T>>): Observable<RemoteData<PaginatedList<T>>> {
|
||||
if (typeof href$ === 'string') {
|
||||
href$ = observableOf(href$);
|
||||
}
|
||||
@@ -132,7 +141,7 @@ export class RemoteDataBuildService {
|
||||
return this.objectCache.getList(resourceUUIDs).pipe(
|
||||
map((normList: Array<NormalizedObject<T>>) => {
|
||||
return normList.map((normalized: NormalizedObject<T>) => {
|
||||
return this.build<T>(normalized);
|
||||
return this.build<T>(normalized, ...linksToFollow);
|
||||
});
|
||||
}));
|
||||
}),
|
||||
@@ -162,8 +171,8 @@ export class RemoteDataBuildService {
|
||||
return this.toRemoteDataObservable(requestEntry$, payload$);
|
||||
}
|
||||
|
||||
build<T extends CacheableObject>(normalized: NormalizedObject<T>): T {
|
||||
const links: any = {};
|
||||
build<T extends CacheableObject>(normalized: NormalizedObject<T>, ...linksToFollow: Array<FollowLinkConfig<T>>): T {
|
||||
const halLinks: any = {};
|
||||
const relationships = getRelationships(normalized.constructor) || [];
|
||||
|
||||
relationships.forEach((relationship: string) => {
|
||||
@@ -207,22 +216,61 @@ export class RemoteDataBuildService {
|
||||
}
|
||||
|
||||
if (hasValue(normalized[relationship].page)) {
|
||||
links[relationship] = this.toPaginatedList(result, normalized[relationship].pageInfo);
|
||||
halLinks[relationship] = this.toPaginatedList(result, normalized[relationship].pageInfo);
|
||||
} else {
|
||||
links[relationship] = result;
|
||||
halLinks[relationship] = result;
|
||||
}
|
||||
} else {
|
||||
if (hasNoValue(links._links)) {
|
||||
links._links = {};
|
||||
if (hasNoValue(halLinks._links)) {
|
||||
halLinks._links = {};
|
||||
}
|
||||
links._links[relationship] = {
|
||||
halLinks._links[relationship] = {
|
||||
href: objectList
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
const domainModel = getMapsTo(normalized.constructor);
|
||||
return Object.assign(new domainModel(), normalized, links);
|
||||
const domainModelConstructor = getMapsTo(normalized.constructor);
|
||||
const domainModel = Object.assign(new domainModelConstructor(), normalized, halLinks);
|
||||
|
||||
linksToFollow.forEach((linkToFollow: FollowLinkConfig<T>) => {
|
||||
this.resolveLink(domainModel, linkToFollow);
|
||||
});
|
||||
|
||||
console.log('domainModel._links', domainModel._links);
|
||||
|
||||
return domainModel;
|
||||
}
|
||||
|
||||
public resolveLink<T extends HALResource>(model, linkToFollow: FollowLinkConfig<T>) {
|
||||
console.log('resolveLink', model, linkToFollow);
|
||||
|
||||
const matchingLink = getLink(model.constructor, linkToFollow.name);
|
||||
|
||||
if (hasNoValue(matchingLink)) {
|
||||
throw new Error(`followLink('${linkToFollow.name}') was used for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`);
|
||||
} else {
|
||||
const provider = getDataServiceFor(matchingLink.targetConstructor);
|
||||
|
||||
if (hasNoValue(provider)) {
|
||||
throw new Error(`The @link() for ${linkToFollow.name} on ${model.constructor.name} models refers to a ${matchingLink.targetConstructor.name}, but there is no service with an @dataService(${matchingLink.targetConstructor.name}) annotation in order to retrieve it`);
|
||||
}
|
||||
|
||||
const service = Injector.create({
|
||||
providers: [],
|
||||
parent: this.parentInjector
|
||||
}).get(provider);
|
||||
|
||||
const href = model._links[matchingLink.linkName].href;
|
||||
|
||||
if (matchingLink.isList) {
|
||||
model[linkToFollow.name] = service.findAllByHref(href, linkToFollow.findListOptions, ...linkToFollow.linksToFollow);
|
||||
} else {
|
||||
model[linkToFollow.name] = service.findByHref(href, ...linkToFollow.linksToFollow);
|
||||
}
|
||||
|
||||
console.log(`model['${linkToFollow.name}']`, model[linkToFollow.name]);
|
||||
}
|
||||
}
|
||||
|
||||
aggregate<T>(input: Array<Observable<RemoteData<T>>>): Observable<RemoteData<T[]>> {
|
||||
|
@@ -25,32 +25,32 @@ export class NormalizedCommunity extends NormalizedDSpaceObject<Community> {
|
||||
* The Bitstream that represents the logo of this Community
|
||||
*/
|
||||
@deserialize
|
||||
@relationship(Bitstream, false)
|
||||
@relationship(Bitstream, false, false)
|
||||
logo: string;
|
||||
|
||||
/**
|
||||
* An array of Communities that are direct parents of this Community
|
||||
*/
|
||||
@deserialize
|
||||
@relationship(Community, true)
|
||||
@relationship(Community, true, false)
|
||||
parents: string[];
|
||||
|
||||
/**
|
||||
* The Community that owns this Community
|
||||
*/
|
||||
@deserialize
|
||||
@relationship(Community, false)
|
||||
@relationship(Community, false, false)
|
||||
owner: string;
|
||||
|
||||
/**
|
||||
* List of Collections that are owned by this Community
|
||||
*/
|
||||
@deserialize
|
||||
@relationship(Collection, true)
|
||||
@relationship(Collection, true, false)
|
||||
collections: string[];
|
||||
|
||||
@deserialize
|
||||
@relationship(Community, true)
|
||||
@relationship(Community, true, false)
|
||||
subcommunities: string[];
|
||||
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { autoserializeAs, deserializeAs, autoserialize } from 'cerialize';
|
||||
import { DSpaceObject } from '../../shared/dspace-object.model';
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
import { MetadataMap, MetadataMapSerializer } from '../../shared/metadata.models';
|
||||
import { mapsTo } from '../builders/build-decorators';
|
||||
import { NormalizedObject } from './normalized-object.model';
|
||||
@@ -67,6 +68,7 @@ export class NormalizedDSpaceObject<T extends DSpaceObject> extends NormalizedOb
|
||||
*/
|
||||
@deserializeAs(Object)
|
||||
_links: {
|
||||
[name: string]: string
|
||||
self: HALLink,
|
||||
[name: string]: HALLink
|
||||
}
|
||||
}
|
||||
|
@@ -44,13 +44,6 @@ export class NormalizedItem extends NormalizedDSpaceObject<Item> {
|
||||
@autoserializeAs(Boolean, 'withdrawn')
|
||||
isWithdrawn: boolean;
|
||||
|
||||
/**
|
||||
* An array of Collections that are direct parents of this Item
|
||||
*/
|
||||
@deserialize
|
||||
@relationship(Collection, true, false)
|
||||
parents: string[];
|
||||
|
||||
/**
|
||||
* The Collection that owns this Item
|
||||
*/
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
import { CacheableObject, TypedObject } from '../object-cache.reducer';
|
||||
import { autoserialize, deserialize } from 'cerialize';
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
@@ -13,7 +14,8 @@ export abstract class NormalizedObject<T extends TypedObject> implements Cacheab
|
||||
|
||||
@deserialize
|
||||
_links: {
|
||||
[name: string]: string
|
||||
self: HALLink,
|
||||
[name: string]: HALLink
|
||||
};
|
||||
|
||||
/**
|
||||
|
8
src/app/core/cache/object-cache.reducer.ts
vendored
8
src/app/core/cache/object-cache.reducer.ts
vendored
@@ -1,3 +1,5 @@
|
||||
import { HALLink } from '../shared/hal-link.model';
|
||||
import { HALResource } from '../shared/hal-resource.model';
|
||||
import {
|
||||
ObjectCacheAction,
|
||||
ObjectCacheActionTypes,
|
||||
@@ -42,10 +44,14 @@ export abstract class TypedObject {
|
||||
*
|
||||
* A cacheable object should have a self link
|
||||
*/
|
||||
export class CacheableObject extends TypedObject {
|
||||
export class CacheableObject extends TypedObject implements HALResource {
|
||||
uuid?: string;
|
||||
handle?: string;
|
||||
self: string;
|
||||
|
||||
_links: {
|
||||
self: HALLink;
|
||||
}
|
||||
// isNew: boolean;
|
||||
// dirtyType: DirtyType;
|
||||
// hasDirtyAttributes: boolean;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
|
||||
export abstract class ConfigObject implements CacheableObject {
|
||||
@@ -11,8 +12,9 @@ export abstract class ConfigObject implements CacheableObject {
|
||||
/**
|
||||
* The links to all related resources returned by the rest api.
|
||||
*/
|
||||
public _links: {
|
||||
[name: string]: string
|
||||
_links: {
|
||||
self: HALLink,
|
||||
[name: string]: HALLink
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { autoserialize, inheritSerialization } from 'cerialize';
|
||||
import { NormalizedObject } from '../../cache/models/normalized-object.model';
|
||||
import { CacheableObject, TypedObject } from '../../cache/object-cache.reducer';
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
|
||||
/**
|
||||
@@ -20,7 +21,8 @@ export abstract class NormalizedConfigObject<T extends CacheableObject> implemen
|
||||
*/
|
||||
@autoserialize
|
||||
public _links: {
|
||||
[name: string]: string
|
||||
self: HALLink,
|
||||
[name: string]: HALLink
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -5,6 +5,8 @@ import { Observable } from 'rxjs/internal/Observable';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { hasNoValue, hasValue } from '../../shared/empty.util';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
@@ -26,6 +28,7 @@ import { RequestService } from './request.service';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@dataService(Bitstream)
|
||||
export class BitstreamDataService extends DataService<Bitstream> {
|
||||
|
||||
protected linkPath = 'bitstreams';
|
||||
@@ -57,8 +60,8 @@ export class BitstreamDataService extends DataService<Bitstream> {
|
||||
* @param bundle the bundle to retrieve bitstreams from
|
||||
* @param options options for the find all request
|
||||
*/
|
||||
findAllByBundle(bundle: Bundle, options?: FindListOptions): Observable<RemoteData<PaginatedList<Bitstream>>> {
|
||||
return this.findAllByHref(bundle._links.bitstreams.href, options);
|
||||
findAllByBundle(bundle: Bundle, options?: FindListOptions, ...linksToFollow: Array<FollowLinkConfig<Bitstream>>): Observable<RemoteData<PaginatedList<Bitstream>>> {
|
||||
return this.findAllByHref(bundle._links.bitstreams.href, options, ...linksToFollow);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,11 +135,11 @@ export class BitstreamDataService extends DataService<Bitstream> {
|
||||
);
|
||||
}
|
||||
|
||||
public findAllByItemAndBundleName(item: Item, bundleName: string, options?: FindListOptions): Observable<RemoteData<PaginatedList<Bitstream>>> {
|
||||
public findAllByItemAndBundleName(item: Item, bundleName: string, options?: FindListOptions, ...linksToFollow: Array<FollowLinkConfig<Bitstream>>): Observable<RemoteData<PaginatedList<Bitstream>>> {
|
||||
return this.bundleService.findByItemAndName(item, bundleName).pipe(
|
||||
switchMap((bundleRD: RemoteData<Bundle>) => {
|
||||
if (hasValue(bundleRD.payload)) {
|
||||
return this.findAllByBundle(bundleRD.payload, options);
|
||||
return this.findAllByBundle(bundleRD.payload, options, ...linksToFollow);
|
||||
} else {
|
||||
return [bundleRD as any];
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { Bitstream } from '../shared/bitstream.model';
|
||||
import { DataService } from './data.service';
|
||||
import { BitstreamFormat } from '../shared/bitstream-format.model';
|
||||
@@ -40,6 +41,7 @@ const selectedBitstreamFormatSelector = createSelector(bitstreamFormatsStateSele
|
||||
* A service responsible for fetching/sending data from/to the REST API on the bitstreamformats endpoint
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(BitstreamFormat)
|
||||
export class BitstreamFormatDataService extends DataService<BitstreamFormat> {
|
||||
|
||||
protected linkPath = 'bitstreamformats';
|
||||
|
@@ -5,6 +5,8 @@ import { Observable } from 'rxjs/internal/Observable';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
@@ -26,6 +28,7 @@ import { RequestService } from './request.service';
|
||||
@Injectable(
|
||||
{providedIn: 'root'}
|
||||
)
|
||||
@dataService(Bundle)
|
||||
export class BundleDataService extends DataService<Bundle> {
|
||||
protected linkPath = 'bundles';
|
||||
protected forceBypassCache = false;
|
||||
@@ -52,13 +55,13 @@ export class BundleDataService extends DataService<Bundle> {
|
||||
return this.halService.getEndpoint(this.linkPath);
|
||||
}
|
||||
|
||||
findAllByItem(item: Item, options?: FindListOptions): Observable<RemoteData<PaginatedList<Bundle>>> {
|
||||
return this.findAllByHref(item._links.bundles.href, options);
|
||||
findAllByItem(item: Item, options?: FindListOptions, ...linksToFollow: Array<FollowLinkConfig<Bundle>>): Observable<RemoteData<PaginatedList<Bundle>>> {
|
||||
return this.findAllByHref(item._links.bundles.href, options, ...linksToFollow);
|
||||
}
|
||||
|
||||
// TODO should be implemented rest side
|
||||
findByItemAndName(item: Item, bundleName: string): Observable<RemoteData<Bundle>> {
|
||||
return this.findAllByItem(item, { elementsPerPage: Number.MAX_SAFE_INTEGER }).pipe(
|
||||
findByItemAndName(item: Item, bundleName: string, ...linksToFollow: Array<FollowLinkConfig<Bundle>>): Observable<RemoteData<Bundle>> {
|
||||
return this.findAllByItem(item, { elementsPerPage: Number.MAX_SAFE_INTEGER }, ...linksToFollow).pipe(
|
||||
map((rd: RemoteData<PaginatedList<Bundle>>) => {
|
||||
if (hasValue(rd.payload) && hasValue(rd.payload.page)) {
|
||||
const matchingBundle = rd.payload.page.find((bundle: Bundle) =>
|
||||
|
@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { distinctUntilChanged, filter, map, switchMap, take } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
@@ -48,6 +49,7 @@ import { INotification } from '../../shared/notifications/models/notification.mo
|
||||
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||
|
||||
@Injectable()
|
||||
@dataService(Collection)
|
||||
export class CollectionDataService extends ComColDataService<Collection> {
|
||||
protected linkPath = 'collections';
|
||||
protected errorTitle = 'collection.source.update.notifications.error.title';
|
||||
|
@@ -8,6 +8,7 @@ import { merge as observableMerge, Observable, throwError as observableThrowErro
|
||||
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||
import { NormalizedCommunity } from '../cache/models/normalized-community.model';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { HALLink } from '../shared/hal-link.model';
|
||||
import { CommunityDataService } from './community-data.service';
|
||||
|
||||
import { DataService } from './data.service';
|
||||
@@ -71,7 +72,8 @@ export abstract class ComColDataService<T extends CacheableObject> extends DataS
|
||||
filter((response) => response.isSuccessful),
|
||||
mergeMap(() => this.objectCache.getObjectByUUID(options.scopeID)),
|
||||
map((nc: NormalizedCommunity) => nc._links[linkPath]),
|
||||
filter((href) => isNotEmpty(href))
|
||||
filter((halLink: HALLink) => isNotEmpty(halLink)),
|
||||
map((halLink: HALLink) => halLink.href)
|
||||
);
|
||||
|
||||
return observableMerge(errorResponses, successResponses).pipe(distinctUntilChanged(), share());
|
||||
|
@@ -2,6 +2,7 @@ import { filter, switchMap, take } from 'rxjs/operators';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
@@ -20,6 +21,7 @@ import { NormalizedObjectBuildService } from '../cache/builders/normalized-objec
|
||||
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
|
||||
|
||||
@Injectable()
|
||||
@dataService(Community)
|
||||
export class CommunityDataService extends ComColDataService<Community> {
|
||||
protected linkPath = 'communities';
|
||||
protected topLinkPath = 'communities/search/top';
|
||||
|
@@ -16,6 +16,7 @@ import {
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { URLCombiner } from '../url-combiner/url-combiner';
|
||||
@@ -30,7 +31,6 @@ import {
|
||||
GetRequest
|
||||
} from './request.models';
|
||||
import { RequestService } from './request.service';
|
||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||
import { NormalizedObject } from '../cache/models/normalized-object.model';
|
||||
import { SearchParam } from '../cache/models/search-param.model';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
@@ -140,11 +140,11 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
}
|
||||
}
|
||||
|
||||
findAll(options: FindListOptions = {}): Observable<RemoteData<PaginatedList<T>>> {
|
||||
return this.findList(this.getFindAllHref(options), options);
|
||||
findAll(options: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<T>>): Observable<RemoteData<PaginatedList<T>>> {
|
||||
return this.findList(this.getFindAllHref(options), options, ...linksToFollow);
|
||||
}
|
||||
|
||||
protected findList(href$, options: FindListOptions) {
|
||||
protected findList(href$, options: FindListOptions, ...linksToFollow: Array<FollowLinkConfig<T>>) {
|
||||
href$.pipe(
|
||||
first((href: string) => hasValue(href)))
|
||||
.subscribe((href: string) => {
|
||||
@@ -155,7 +155,7 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
this.requestService.configure(request);
|
||||
});
|
||||
|
||||
return this.rdbService.buildList<T>(href$) as Observable<RemoteData<PaginatedList<T>>>;
|
||||
return this.rdbService.buildList<T>(href$, ...linksToFollow) as Observable<RemoteData<PaginatedList<T>>>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,7 +167,7 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
return `${endpoint}/${resourceID}`;
|
||||
}
|
||||
|
||||
findById(id: string): Observable<RemoteData<T>> {
|
||||
findById(id: string, ...linksToFollow: Array<FollowLinkConfig<T>>): Observable<RemoteData<T>> {
|
||||
|
||||
const hrefObs = this.halService.getEndpoint(this.linkPath).pipe(
|
||||
map((endpoint: string) => this.getIDHref(endpoint, encodeURIComponent(id))));
|
||||
@@ -182,27 +182,27 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
this.requestService.configure(request);
|
||||
});
|
||||
|
||||
return this.rdbService.buildSingle<T>(hrefObs);
|
||||
return this.rdbService.buildSingle<T>(hrefObs, ...linksToFollow);
|
||||
}
|
||||
|
||||
findByHref(href: string, findListOptions: FindListOptions = {}, httpOptions?: HttpOptions): Observable<RemoteData<T>> {
|
||||
const requestHref = this.buildHrefFromFindOptions(href, findListOptions, []);
|
||||
const request = new GetRequest(this.requestService.generateRequestId(), requestHref, null, httpOptions);
|
||||
findByHref(href: string, ...linksToFollow: Array<FollowLinkConfig<T>>): Observable<RemoteData<T>> {
|
||||
const requestHref = this.buildHrefFromFindOptions(href, {}, []);
|
||||
const request = new GetRequest(this.requestService.generateRequestId(), requestHref);
|
||||
if (hasValue(this.responseMsToLive)) {
|
||||
request.responseMsToLive = this.responseMsToLive;
|
||||
}
|
||||
this.requestService.configure(request);
|
||||
return this.rdbService.buildSingle<T>(href);
|
||||
return this.rdbService.buildSingle<T>(href, ...linksToFollow);
|
||||
}
|
||||
|
||||
findAllByHref(href: string, findListOptions: FindListOptions = {}, httpOptions?: HttpOptions): Observable<RemoteData<PaginatedList<T>>> {
|
||||
findAllByHref(href: string, findListOptions: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<T>>): Observable<RemoteData<PaginatedList<T>>> {
|
||||
const requestHref = this.buildHrefFromFindOptions(href, findListOptions, []);
|
||||
const request = new GetRequest(this.requestService.generateRequestId(), requestHref, null, httpOptions);
|
||||
const request = new GetRequest(this.requestService.generateRequestId(), requestHref);
|
||||
if (hasValue(this.responseMsToLive)) {
|
||||
request.responseMsToLive = this.responseMsToLive;
|
||||
}
|
||||
this.requestService.configure(request);
|
||||
return this.rdbService.buildList<T>(requestHref);
|
||||
return this.rdbService.buildList<T>(requestHref, ...linksToFollow);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +224,7 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
* @return {Observable<RemoteData<PaginatedList<T>>}
|
||||
* Return an observable that emits response from the server
|
||||
*/
|
||||
protected searchBy(searchMethod: string, options: FindListOptions = {}): Observable<RemoteData<PaginatedList<T>>> {
|
||||
protected searchBy(searchMethod: string, options: FindListOptions = {}, ...linksToFollow: Array<FollowLinkConfig<T>>): Observable<RemoteData<PaginatedList<T>>> {
|
||||
|
||||
const hrefObs = this.getSearchByHref(searchMethod, options);
|
||||
|
||||
@@ -241,7 +241,7 @@ export abstract class DataService<T extends CacheableObject> {
|
||||
switchMap((href) => this.requestService.getByHref(href)),
|
||||
skipWhile((requestEntry) => hasValue(requestEntry) && requestEntry.completed),
|
||||
switchMap((href) =>
|
||||
this.rdbService.buildList<T>(hrefObs) as Observable<RemoteData<PaginatedList<T>>>
|
||||
this.rdbService.buildList<T>(hrefObs, ...linksToFollow) as Observable<RemoteData<PaginatedList<T>>>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ describe('DsoRedirectDataService', () => {
|
||||
describe('findById', () => {
|
||||
it('should call HALEndpointService with the path to the pid endpoint', () => {
|
||||
setup();
|
||||
scheduler.schedule(() => service.findById(dsoHandle, IdentifierType.HANDLE));
|
||||
scheduler.schedule(() => service.findByIdAndIDType(dsoHandle, IdentifierType.HANDLE));
|
||||
scheduler.flush();
|
||||
|
||||
expect(halService.getEndpoint).toHaveBeenCalledWith('pid');
|
||||
@@ -91,7 +91,7 @@ describe('DsoRedirectDataService', () => {
|
||||
|
||||
it('should call HALEndpointService with the path to the dso endpoint', () => {
|
||||
setup();
|
||||
scheduler.schedule(() => service.findById(dsoUUID, IdentifierType.UUID));
|
||||
scheduler.schedule(() => service.findByIdAndIDType(dsoUUID, IdentifierType.UUID));
|
||||
scheduler.flush();
|
||||
|
||||
expect(halService.getEndpoint).toHaveBeenCalledWith('dso');
|
||||
@@ -99,7 +99,7 @@ describe('DsoRedirectDataService', () => {
|
||||
|
||||
it('should call HALEndpointService with the path to the dso endpoint when identifier type not specified', () => {
|
||||
setup();
|
||||
scheduler.schedule(() => service.findById(dsoUUID));
|
||||
scheduler.schedule(() => service.findByIdAndIDType(dsoUUID));
|
||||
scheduler.flush();
|
||||
|
||||
expect(halService.getEndpoint).toHaveBeenCalledWith('dso');
|
||||
@@ -107,7 +107,7 @@ describe('DsoRedirectDataService', () => {
|
||||
|
||||
it('should configure the proper FindByIDRequest for uuid', () => {
|
||||
setup();
|
||||
scheduler.schedule(() => service.findById(dsoUUID, IdentifierType.UUID));
|
||||
scheduler.schedule(() => service.findByIdAndIDType(dsoUUID, IdentifierType.UUID));
|
||||
scheduler.flush();
|
||||
|
||||
expect(requestService.configure).toHaveBeenCalledWith(new FindByIDRequest(requestUUID, requestUUIDURL, dsoUUID));
|
||||
@@ -115,7 +115,7 @@ describe('DsoRedirectDataService', () => {
|
||||
|
||||
it('should configure the proper FindByIDRequest for handle', () => {
|
||||
setup();
|
||||
scheduler.schedule(() => service.findById(dsoHandle, IdentifierType.HANDLE));
|
||||
scheduler.schedule(() => service.findByIdAndIDType(dsoHandle, IdentifierType.HANDLE));
|
||||
scheduler.flush();
|
||||
|
||||
expect(requestService.configure).toHaveBeenCalledWith(new FindByIDRequest(requestUUID, requestHandleURL, dsoHandle));
|
||||
@@ -124,7 +124,7 @@ describe('DsoRedirectDataService', () => {
|
||||
it('should navigate to item route', () => {
|
||||
remoteData.payload.type = 'item';
|
||||
setup();
|
||||
const redir = service.findById(dsoHandle, IdentifierType.HANDLE);
|
||||
const redir = service.findByIdAndIDType(dsoHandle, IdentifierType.HANDLE);
|
||||
// The framework would normally subscribe but do it here so we can test navigation.
|
||||
redir.subscribe();
|
||||
scheduler.schedule(() => redir);
|
||||
@@ -135,7 +135,7 @@ describe('DsoRedirectDataService', () => {
|
||||
it('should navigate to collections route', () => {
|
||||
remoteData.payload.type = 'collection';
|
||||
setup();
|
||||
const redir = service.findById(dsoHandle, IdentifierType.HANDLE);
|
||||
const redir = service.findByIdAndIDType(dsoHandle, IdentifierType.HANDLE);
|
||||
redir.subscribe();
|
||||
scheduler.schedule(() => redir);
|
||||
scheduler.flush();
|
||||
@@ -145,7 +145,7 @@ describe('DsoRedirectDataService', () => {
|
||||
it('should navigate to communities route', () => {
|
||||
remoteData.payload.type = 'community';
|
||||
setup();
|
||||
const redir = service.findById(dsoHandle, IdentifierType.HANDLE);
|
||||
const redir = service.findByIdAndIDType(dsoHandle, IdentifierType.HANDLE);
|
||||
redir.subscribe();
|
||||
scheduler.schedule(() => redir);
|
||||
scheduler.flush();
|
||||
|
@@ -58,9 +58,9 @@ export class DsoRedirectDataService extends DataService<any> {
|
||||
.replace(/\{\?uuid\}/, `?uuid=${resourceID}`);
|
||||
}
|
||||
|
||||
findById(id: string, identifierType = IdentifierType.UUID): Observable<RemoteData<FindByIDRequest>> {
|
||||
findByIdAndIDType(id: string, identifierType = IdentifierType.UUID): Observable<RemoteData<FindByIDRequest>> {
|
||||
this.setLinkPath(identifierType);
|
||||
return super.findById(id).pipe(
|
||||
return this.findById(id).pipe(
|
||||
getFinishedRemoteData(),
|
||||
take(1),
|
||||
tap((response) => {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { DSpaceObject } from '../shared/dspace-object.model';
|
||||
@@ -42,6 +43,7 @@ class DataServiceImpl extends DataService<DSpaceObject> {
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@dataService(DSpaceObject)
|
||||
export class DSpaceObjectDataService {
|
||||
protected linkPath = 'dso';
|
||||
private dataService: DataServiceImpl;
|
||||
|
@@ -4,6 +4,7 @@ import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs';
|
||||
import { hasValue, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { BrowseService } from '../browse/browse.service';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { Item } from '../shared/item.model';
|
||||
@@ -40,6 +41,7 @@ import { PaginatedList } from './paginated-list';
|
||||
import { ExternalSourceEntry } from '../shared/external-source-entry.model';
|
||||
|
||||
@Injectable()
|
||||
@dataService(Item)
|
||||
export class ItemDataService extends DataService<Item> {
|
||||
protected linkPath = 'items';
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
|
||||
@@ -42,6 +43,7 @@ class DataServiceImpl extends DataService<MetadataSchema> {
|
||||
* A service responsible for fetching/sending data from/to the REST API on the metadataschemas endpoint
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(MetadataSchema)
|
||||
export class MetadataSchemaDataService {
|
||||
private dataService: DataServiceImpl;
|
||||
|
||||
|
@@ -1,47 +1,36 @@
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ReorderableRelationship } from '../../shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component';
|
||||
import { RequestService } from './request.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { hasValue, hasValueOperator, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
||||
import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, map, mergeMap, startWith, switchMap, take, tap } from 'rxjs/operators';
|
||||
import {
|
||||
configureRequest,
|
||||
getRemoteDataPayload,
|
||||
getResponseFromEntry,
|
||||
getSucceededRemoteData
|
||||
} from '../shared/operators';
|
||||
import { compareArraysUsingIds, paginatedRelationsToItems, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||
import { AppState, keySelector } from '../../app.reducer';
|
||||
import { hasValue, hasValueOperator, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util';
|
||||
import { ReorderableRelationship } from '../../shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component';
|
||||
import { RemoveNameVariantAction, SetNameVariantAction } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.actions';
|
||||
import { NameVariantListState } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { configureRequest, getRemoteDataPayload, getResponseFromEntry, getSucceededRemoteData } from '../shared/operators';
|
||||
import { SearchParam } from '../cache/models/search-param.model';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { DeleteRequest, FindListOptions, PostRequest, RestRequest } from './request.models';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { RestResponse } from '../cache/response.models';
|
||||
import { Item } from '../shared/item.model';
|
||||
import { Relationship } from '../shared/item-relationships/relationship.model';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RelationshipType } from '../shared/item-relationships/relationship-type.model';
|
||||
import { RemoteData, RemoteDataState } from './remote-data';
|
||||
import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
|
||||
import { PaginatedList } from './paginated-list';
|
||||
import { ItemDataService } from './item-data.service';
|
||||
import {
|
||||
compareArraysUsingIds,
|
||||
paginatedRelationsToItems,
|
||||
relationsToItems
|
||||
} from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { Relationship } from '../shared/item-relationships/relationship.model';
|
||||
import { Item } from '../shared/item.model';
|
||||
import { DataService } from './data.service';
|
||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||
import { MemoizedSelector, select, Store } from '@ngrx/store';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
||||
import { SearchParam } from '../cache/models/search-param.model';
|
||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||
import { AppState, keySelector } from '../../app.reducer';
|
||||
import { NameVariantListState } from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.reducer';
|
||||
import {
|
||||
RemoveNameVariantAction,
|
||||
SetNameVariantAction
|
||||
} from '../../shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/name-variant.actions';
|
||||
import { RequestService } from './request.service';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
|
||||
const relationshipListsStateSelector = (state: AppState) => state.relationshipLists;
|
||||
|
||||
@@ -57,9 +46,9 @@ const relationshipStateSelector = (listID: string, itemID: string): MemoizedSele
|
||||
* The service handling all relationship requests
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(Relationship)
|
||||
export class RelationshipService extends DataService<Relationship> {
|
||||
protected linkPath = 'relationships';
|
||||
protected forceBypassCache = false;
|
||||
|
||||
constructor(protected itemService: ItemDataService,
|
||||
protected requestService: RequestService,
|
||||
|
@@ -7,6 +7,7 @@ import { Observable } from 'rxjs';
|
||||
import { DataService } from '../data/data.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { FindListOptions } from '../data/request.models';
|
||||
import { Collection } from '../shared/collection.model';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { ResourcePolicy } from '../shared/resource-policy.model';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
@@ -17,7 +18,7 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
|
||||
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
|
||||
import { ChangeAnalyzer } from './change-analyzer';
|
||||
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
|
||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||
import { PaginatedList } from './paginated-list';
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
class DataServiceImpl extends DataService<ResourcePolicy> {
|
||||
@@ -60,7 +61,11 @@ export class ResourcePolicyService {
|
||||
this.dataService = new DataServiceImpl(requestService, rdbService, dataBuildService, null, objectCache, halService, notificationsService, http, comparator);
|
||||
}
|
||||
|
||||
findByHref(href: string, options?: HttpOptions): Observable<RemoteData<ResourcePolicy>> {
|
||||
return this.dataService.findByHref(href, {}, options);
|
||||
findByHref(href: string): Observable<RemoteData<ResourcePolicy>> {
|
||||
return this.dataService.findByHref(href);
|
||||
}
|
||||
|
||||
getDefaultAccessConditionsFor(collection: Collection, findListOptions?: FindListOptions): Observable<RemoteData<PaginatedList<ResourcePolicy>>> {
|
||||
return this.dataService.findAllByHref(collection._links.defaultAccessConditions.href, findListOptions);
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { DataService } from './data.service';
|
||||
import { Site } from '../shared/site.model';
|
||||
import { RequestService } from './request.service';
|
||||
@@ -22,6 +23,7 @@ import { getSucceededRemoteData } from '../shared/operators';
|
||||
* Service responsible for handling requests related to the Site object
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(Site)
|
||||
export class SiteDataService extends DataService<Site> {
|
||||
protected linkPath = 'sites';
|
||||
protected forceBypassCache = false;
|
||||
|
@@ -77,14 +77,16 @@ export class DSpaceRESTv2Serializer<T> implements Serializer<T> {
|
||||
}
|
||||
|
||||
private normalizeLinks(links: any): any {
|
||||
const normalizedLinks = links;
|
||||
for (const link in normalizedLinks) {
|
||||
if (Array.isArray(normalizedLinks[link])) {
|
||||
normalizedLinks[link] = normalizedLinks[link].map((linkedResource) => {
|
||||
return linkedResource.href;
|
||||
});
|
||||
} else {
|
||||
normalizedLinks[link] = normalizedLinks[link].href;
|
||||
const normalizedLinks = {};
|
||||
for (const link in links) {
|
||||
if (links.hasOwnProperty(link)) {
|
||||
if (Array.isArray(links[link])) {
|
||||
normalizedLinks[link] = links[link].map((linkedResource) => {
|
||||
return linkedResource.href;
|
||||
});
|
||||
} else {
|
||||
normalizedLinks[link] = links[link].href;
|
||||
}
|
||||
}
|
||||
}
|
||||
return normalizedLinks;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { autoserialize } from 'cerialize';
|
||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
|
||||
export abstract class IntegrationModel implements CacheableObject {
|
||||
|
||||
@@ -14,7 +15,8 @@ export abstract class IntegrationModel implements CacheableObject {
|
||||
|
||||
@autoserialize
|
||||
public _links: {
|
||||
[name: string]: string
|
||||
self: HALLink,
|
||||
[name: string]: HALLink
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,5 +1,8 @@
|
||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { link } from '../cache/builders/build-decorators';
|
||||
import { HALLink } from '../shared/hal-link.model';
|
||||
import { HALResource } from '../shared/hal-resource.model';
|
||||
import { MetadataSchema } from './metadata-schema.model';
|
||||
import { ResourceType } from '../shared/resource-type';
|
||||
import { GenericConstructor } from '../shared/generic-constructor';
|
||||
@@ -7,7 +10,7 @@ import { GenericConstructor } from '../shared/generic-constructor';
|
||||
/**
|
||||
* Class the represents a metadata field
|
||||
*/
|
||||
export class MetadataField extends ListableObject {
|
||||
export class MetadataField extends ListableObject implements HALResource {
|
||||
static type = new ResourceType('metadatafield');
|
||||
|
||||
/**
|
||||
@@ -38,7 +41,14 @@ export class MetadataField extends ListableObject {
|
||||
/**
|
||||
* The metadata schema object of this metadata field
|
||||
*/
|
||||
schema: MetadataSchema;
|
||||
@link(MetadataSchema)
|
||||
// TODO the responseparsingservice assumes schemas are always embedded. This should be remotedata instead.
|
||||
schema?: MetadataSchema;
|
||||
|
||||
_links: {
|
||||
self: HALLink,
|
||||
schema: HALLink
|
||||
};
|
||||
|
||||
/**
|
||||
* Method to print this metadata field as a string
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||
import { HALLink } from '../shared/hal-link.model';
|
||||
import { HALResource } from '../shared/hal-resource.model';
|
||||
import { ResourceType } from '../shared/resource-type';
|
||||
import { GenericConstructor } from '../shared/generic-constructor';
|
||||
|
||||
/**
|
||||
* Class that represents a metadata schema
|
||||
*/
|
||||
export class MetadataSchema extends ListableObject {
|
||||
export class MetadataSchema extends ListableObject implements HALResource {
|
||||
static type = new ResourceType('metadataschema');
|
||||
|
||||
/**
|
||||
@@ -28,6 +30,10 @@ export class MetadataSchema extends ListableObject {
|
||||
*/
|
||||
namespace: string;
|
||||
|
||||
_links: {
|
||||
self: HALLink,
|
||||
};
|
||||
|
||||
/**
|
||||
* Method that returns as which type of object this object should be rendered
|
||||
*/
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { CacheableObject, TypedObject } from '../cache/object-cache.reducer';
|
||||
import { HALLink } from './hal-link.model';
|
||||
import { ResourceType } from './resource-type';
|
||||
import { BitstreamFormatSupportLevel } from './bitstream-format-support-level';
|
||||
|
||||
@@ -56,4 +57,7 @@ export class BitstreamFormat implements CacheableObject {
|
||||
*/
|
||||
id: string;
|
||||
|
||||
_links: {
|
||||
self: HALLink;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,14 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { link } from '../cache/builders/build-decorators';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { BitstreamFormat } from './bitstream-format.model';
|
||||
import { Bundle } from './bundle.model';
|
||||
import { DSpaceObject } from './dspace-object.model';
|
||||
import { HALLink } from './HALLink.model';
|
||||
import { HALResource } from './hal-resource.model';
|
||||
import { HALLink } from './hal-link.model';
|
||||
import { ResourceType } from './resource-type';
|
||||
|
||||
export class Bitstream extends DSpaceObject {
|
||||
export class Bitstream extends DSpaceObject implements HALResource {
|
||||
static type = new ResourceType('bitstream');
|
||||
|
||||
/**
|
||||
@@ -31,17 +34,19 @@ export class Bitstream extends DSpaceObject {
|
||||
/**
|
||||
* The Bitstream Format for this Bitstream
|
||||
*/
|
||||
@link(BitstreamFormat)
|
||||
format?: Observable<RemoteData<BitstreamFormat>>;
|
||||
|
||||
/**
|
||||
* The URL to retrieve this Bitstream's file
|
||||
*/
|
||||
content: string;
|
||||
|
||||
_links: {
|
||||
// @link(Bitstream)
|
||||
self: HALLink;
|
||||
|
||||
// @link(Bundle)
|
||||
bundle: HALLink;
|
||||
content: HALLink;
|
||||
|
||||
// @link(BitstreamFormat)
|
||||
format: HALLink;
|
||||
|
||||
content: HALLink;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { DSpaceObject } from './dspace-object.model';
|
||||
import { HALLink } from './HALLink.model';
|
||||
import { HALLink } from './hal-link.model';
|
||||
import { ResourceType } from './resource-type';
|
||||
|
||||
export class Bundle extends DSpaceObject {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { link } from '../cache/builders/build-decorators';
|
||||
import { DSpaceObject } from './dspace-object.model';
|
||||
import { Bitstream } from './bitstream.model';
|
||||
import { HALLink } from './HALLink.model';
|
||||
import { HALLink } from './hal-link.model';
|
||||
import { Item } from './item.model';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { Observable } from 'rxjs';
|
||||
@@ -60,29 +61,19 @@ export class Collection extends DSpaceObject {
|
||||
/**
|
||||
* The deposit license of this Collection
|
||||
*/
|
||||
license: Observable<RemoteData<License>>;
|
||||
// license?: Observable<RemoteData<License>>;
|
||||
|
||||
/**
|
||||
* The Bitstream that represents the logo of this Collection
|
||||
*/
|
||||
logo: Observable<RemoteData<Bitstream>>;
|
||||
@link(Bitstream)
|
||||
logo?: Observable<RemoteData<Bitstream>>;
|
||||
|
||||
/**
|
||||
* The default access conditions of this Collection
|
||||
*/
|
||||
defaultAccessConditions: Observable<RemoteData<PaginatedList<ResourcePolicy>>>;
|
||||
|
||||
/**
|
||||
* An array of Collections that are direct parents of this Collection
|
||||
*/
|
||||
parents: Observable<RemoteData<Collection[]>>;
|
||||
|
||||
/**
|
||||
* The Collection that owns this Collection
|
||||
*/
|
||||
owner: Observable<RemoteData<Collection>>;
|
||||
|
||||
items: Observable<RemoteData<Item[]>>;
|
||||
@link(ResourcePolicy, true)
|
||||
defaultAccessConditions?: Observable<RemoteData<PaginatedList<ResourcePolicy>>>;
|
||||
|
||||
_links: {
|
||||
license: HALLink;
|
||||
|
@@ -1,9 +1,11 @@
|
||||
import { link } from '../cache/builders/build-decorators';
|
||||
import { DSpaceObject } from './dspace-object.model';
|
||||
import { Bitstream } from './bitstream.model';
|
||||
import { Collection } from './collection.model';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { Observable } from 'rxjs';
|
||||
import { PaginatedList } from '../data/paginated-list';
|
||||
import { HALLink } from './hal-link.model';
|
||||
import { ResourceType } from './resource-type';
|
||||
|
||||
export class Community extends DSpaceObject {
|
||||
@@ -49,20 +51,19 @@ export class Community extends DSpaceObject {
|
||||
/**
|
||||
* The Bitstream that represents the logo of this Community
|
||||
*/
|
||||
logo: Observable<RemoteData<Bitstream>>;
|
||||
@link(Bitstream)
|
||||
logo?: Observable<RemoteData<Bitstream>>;
|
||||
|
||||
/**
|
||||
* An array of Communities that are direct parents of this Community
|
||||
*/
|
||||
parents: Observable<RemoteData<DSpaceObject[]>>;
|
||||
@link(Collection, true)
|
||||
collections?: Observable<RemoteData<PaginatedList<Collection>>>;
|
||||
|
||||
/**
|
||||
* The Community that owns this Community
|
||||
*/
|
||||
owner: Observable<RemoteData<Community>>;
|
||||
|
||||
collections: Observable<RemoteData<PaginatedList<Collection>>>;
|
||||
|
||||
subcommunities: Observable<RemoteData<PaginatedList<Community>>>;
|
||||
@link(Community, true)
|
||||
subcommunities?: Observable<RemoteData<PaginatedList<Community>>>;
|
||||
|
||||
_links: {
|
||||
collections: HALLink;
|
||||
logo: HALLink;
|
||||
subcommunities: HALLink;
|
||||
self: HALLink;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { GenericConstructor } from './generic-constructor';
|
||||
import { HALLink } from './hal-link.model';
|
||||
import {
|
||||
MetadataMap,
|
||||
MetadataValue,
|
||||
@@ -9,11 +9,9 @@ import {
|
||||
import { Metadata } from './metadata.utils';
|
||||
import { hasNoValue, isUndefined } from '../../shared/empty.util';
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||
import { excludeFromEquals } from '../utilities/equals.decorators';
|
||||
import { ResourceType } from './resource-type';
|
||||
import { GenericConstructor } from './generic-constructor';
|
||||
|
||||
/**
|
||||
* An abstract model class for a DSpaceObject.
|
||||
@@ -67,6 +65,10 @@ export class DSpaceObject extends ListableObject implements CacheableObject {
|
||||
@excludeFromEquals
|
||||
metadata: MetadataMap;
|
||||
|
||||
_links: {
|
||||
self: HALLink,
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the current metadata as a list of MetadatumViewModels
|
||||
*/
|
||||
@@ -74,18 +76,6 @@ export class DSpaceObject extends ListableObject implements CacheableObject {
|
||||
return Metadata.toViewModelList(this.metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of DSpaceObjects that are direct parents of this DSpaceObject
|
||||
*/
|
||||
@excludeFromEquals
|
||||
parents: Observable<RemoteData<DSpaceObject[]>>;
|
||||
|
||||
/**
|
||||
* The DSpaceObject that owns this DSpaceObject
|
||||
*/
|
||||
@excludeFromEquals
|
||||
owner: Observable<RemoteData<DSpaceObject>>;
|
||||
|
||||
/**
|
||||
* Gets all matching metadata in this DSpaceObject.
|
||||
*
|
||||
|
8
src/app/core/shared/hal-resource.model.ts
Normal file
8
src/app/core/shared/hal-resource.model.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { HALLink } from './hal-link.model';
|
||||
|
||||
export class HALResource {
|
||||
_links: {
|
||||
self: HALLink
|
||||
[k: string]: HALLink;
|
||||
};
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||
import { HALLink } from '../hal-link.model';
|
||||
import { ResourceType } from '../resource-type';
|
||||
|
||||
/**
|
||||
@@ -23,4 +24,8 @@ export class ItemType implements CacheableObject {
|
||||
* The universally unique identifier of this ItemType
|
||||
*/
|
||||
uuid: string;
|
||||
|
||||
_links: {
|
||||
self: HALLink,
|
||||
};
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { link } from '../../cache/builders/build-decorators';
|
||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { HALLink } from '../hal-link.model';
|
||||
import { ResourceType } from '../resource-type';
|
||||
import { ItemType } from './item-type.model';
|
||||
|
||||
@@ -63,10 +65,18 @@ export class RelationshipType implements CacheableObject {
|
||||
/**
|
||||
* The type of Item found to the left of this RelationshipType
|
||||
*/
|
||||
leftType: Observable<RemoteData<ItemType>>;
|
||||
@link(ItemType)
|
||||
leftType?: Observable<RemoteData<ItemType>>;
|
||||
|
||||
/**
|
||||
* The type of Item found to the right of this RelationshipType
|
||||
*/
|
||||
rightType: Observable<RemoteData<ItemType>>;
|
||||
@link(ItemType)
|
||||
rightType?: Observable<RemoteData<ItemType>>;
|
||||
|
||||
_links: {
|
||||
self: HALLink,
|
||||
leftType: HALLink,
|
||||
rightType: HALLink,
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { link } from '../../cache/builders/build-decorators';
|
||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { HALLink } from '../hal-link.model';
|
||||
import { ResourceType } from '../resource-type';
|
||||
import { RelationshipType } from './relationship-type.model';
|
||||
import { Item } from '../item.model';
|
||||
@@ -29,12 +31,14 @@ export class Relationship implements CacheableObject {
|
||||
/**
|
||||
* The item to the left of this relationship
|
||||
*/
|
||||
leftItem: Observable<RemoteData<Item>>;
|
||||
@link(Item)
|
||||
leftItem?: Observable<RemoteData<Item>>;
|
||||
|
||||
/**
|
||||
* The item to the right of this relationship
|
||||
*/
|
||||
rightItem: Observable<RemoteData<Item>>;
|
||||
@link(Item)
|
||||
rightItem?: Observable<RemoteData<Item>>;
|
||||
|
||||
/**
|
||||
* The place of the Item to the left side of this Relationship
|
||||
@@ -59,5 +63,14 @@ export class Relationship implements CacheableObject {
|
||||
/**
|
||||
* The type of Relationship
|
||||
*/
|
||||
relationshipType: Observable<RemoteData<RelationshipType>>;
|
||||
@link(RelationshipType)
|
||||
relationshipType?: Observable<RemoteData<RelationshipType>>;
|
||||
|
||||
_links: {
|
||||
self: HALLink,
|
||||
leftItem: HALLink,
|
||||
rightItem: HALLink,
|
||||
relationshipType: HALLink,
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,10 +1,16 @@
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { isEmpty } from '../../shared/empty.util';
|
||||
import { DEFAULT_ENTITY_TYPE } from '../../shared/metadata-representation/metadata-representation.decorator';
|
||||
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
|
||||
import { link } from '../cache/builders/build-decorators';
|
||||
import { PaginatedList } from '../data/paginated-list';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { Bundle } from './bundle.model';
|
||||
|
||||
import { DSpaceObject } from './dspace-object.model';
|
||||
import { GenericConstructor } from './generic-constructor';
|
||||
import { HALLink } from './HALLink.model';
|
||||
import { HALLink } from './hal-link.model';
|
||||
import { Relationship } from './item-relationships/relationship.model';
|
||||
import { ResourceType } from './resource-type';
|
||||
|
||||
/**
|
||||
@@ -38,12 +44,19 @@ export class Item extends DSpaceObject {
|
||||
*/
|
||||
isWithdrawn: boolean;
|
||||
|
||||
@link(Bundle, true)
|
||||
bundles: Observable<RemoteData<PaginatedList<Bundle>>>;
|
||||
|
||||
@link(Relationship, true)
|
||||
relationships: Observable<RemoteData<PaginatedList<Relationship>>>;
|
||||
|
||||
_links: {
|
||||
self: HALLink;
|
||||
parents: HALLink;
|
||||
owningCollection: HALLink;
|
||||
bundles: HALLink;
|
||||
mappedCollections: HALLink;
|
||||
relationships: HALLink;
|
||||
bundles: HALLink;
|
||||
owningCollection: HALLink;
|
||||
templateItemOf: HALLink;
|
||||
self: HALLink;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { HALLink } from './hal-link.model';
|
||||
import { ResourceType } from './resource-type';
|
||||
import { ActionType } from '../cache/models/action-type.model';
|
||||
|
||||
@@ -33,4 +34,7 @@ export class ResourcePolicy implements CacheableObject {
|
||||
*/
|
||||
uuid: string;
|
||||
|
||||
_links: {
|
||||
self: HALLink,
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ import { combineLatest as observableCombineLatest, Observable, of as observableO
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import { NavigationExtras, Router } from '@angular/router';
|
||||
import { first, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||
import { FacetConfigSuccessResponse, FacetValueSuccessResponse, SearchSuccessResponse } from '../../cache/response.models';
|
||||
import { PaginatedList } from '../../data/paginated-list';
|
||||
import { ResponseParsingService } from '../../data/parsing.service';
|
||||
@@ -340,6 +341,8 @@ export class SearchService implements OnDestroy {
|
||||
switchMap((dsoRD: RemoteData<DSpaceObject>) => {
|
||||
if ((dsoRD.payload as any).type === Community.type.value) {
|
||||
const community: Community = dsoRD.payload as Community;
|
||||
this.rdb.resolveLink(community, followLink('subcommunities'));
|
||||
this.rdb.resolveLink(community, followLink('collections'));
|
||||
return observableCombineLatest(community.subcommunities, community.collections).pipe(
|
||||
map(([subCommunities, collections]) => {
|
||||
/*if this is a community, we also need to show the direct children*/
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { link } from '../../cache/builders/build-decorators';
|
||||
|
||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||
import { DSpaceObject } from '../../shared/dspace-object.model';
|
||||
import { EPerson } from '../../eperson/models/eperson.model';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { Collection } from '../../shared/collection.model';
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
import { Item } from '../../shared/item.model';
|
||||
import { SubmissionDefinitionsModel } from '../../config/models/config-submission-definitions.model';
|
||||
import { WorkspaceitemSectionsObject } from './workspaceitem-sections.model';
|
||||
@@ -37,12 +39,14 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
|
||||
/**
|
||||
* The collection this submission applies to
|
||||
*/
|
||||
collection: Observable<RemoteData<Collection>> | Collection;
|
||||
@link(Collection)
|
||||
collection?: Observable<RemoteData<Collection>> | Collection;
|
||||
|
||||
/**
|
||||
* The submission item
|
||||
*/
|
||||
item: Observable<RemoteData<Item>> | Item;
|
||||
@link(Item)
|
||||
item?: Observable<RemoteData<Item>> | Item;
|
||||
|
||||
/**
|
||||
* The workspaceitem/workflowitem last sections data
|
||||
@@ -52,15 +56,26 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
|
||||
/**
|
||||
* The configuration object that define this submission
|
||||
*/
|
||||
submissionDefinition: Observable<RemoteData<SubmissionDefinitionsModel>> | SubmissionDefinitionsModel;
|
||||
@link(SubmissionDefinitionsModel)
|
||||
submissionDefinition?: Observable<RemoteData<SubmissionDefinitionsModel>> | SubmissionDefinitionsModel;
|
||||
|
||||
/**
|
||||
* The workspaceitem submitter
|
||||
*/
|
||||
submitter: Observable<RemoteData<EPerson>> | EPerson;
|
||||
@link(EPerson)
|
||||
submitter?: Observable<RemoteData<EPerson>> | EPerson;
|
||||
|
||||
/**
|
||||
* The workspaceitem/workflowitem last sections errors
|
||||
*/
|
||||
errors: SubmissionObjectError[];
|
||||
|
||||
_links: {
|
||||
self: HALLink,
|
||||
collection: HALLink,
|
||||
item: HALLink,
|
||||
submissionDefinition: HALLink,
|
||||
submitter: HALLink,
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ describe('SubmissionObjectDataService', () => {
|
||||
service = new SubmissionObjectDataService(workspaceitemDataService, workflowItemDataService, submissionService);
|
||||
});
|
||||
|
||||
it('should forward the result of WorkspaceitemDataService.findById()', () => {
|
||||
it('should forward the result of WorkspaceitemDataService.findByIdAndIDType()', () => {
|
||||
const result = service.findById(submissionId);
|
||||
expect(workspaceitemDataService.findById).toHaveBeenCalledWith(submissionId);
|
||||
expect(result).toBe(wsiResult);
|
||||
@@ -60,7 +60,7 @@ describe('SubmissionObjectDataService', () => {
|
||||
service = new SubmissionObjectDataService(workspaceitemDataService, workflowItemDataService, submissionService);
|
||||
});
|
||||
|
||||
it('should forward the result of WorkflowItemDataService.findById()', () => {
|
||||
it('should forward the result of WorkflowItemDataService.findByIdAndIDType()', () => {
|
||||
const result = service.findById(submissionId);
|
||||
expect(workflowItemDataService.findById).toHaveBeenCalledWith(submissionId);
|
||||
expect(result).toBe(wfiResult);
|
||||
|
@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { DataService } from '../data/data.service';
|
||||
@@ -18,6 +19,7 @@ import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
|
||||
* A service that provides methods to make REST requests with workflowitems endpoint.
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(WorkflowItem)
|
||||
export class WorkflowItemDataService extends DataService<WorkflowItem> {
|
||||
protected linkPath = 'workflowitems';
|
||||
protected responseMsToLive = 10 * 1000;
|
||||
|
@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { DataService } from '../data/data.service';
|
||||
@@ -18,6 +19,7 @@ import { WorkspaceItem } from './models/workspaceitem.model';
|
||||
* A service that provides methods to make REST requests with workspaceitems endpoint.
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(WorkspaceItem)
|
||||
export class WorkspaceitemDataService extends DataService<WorkspaceItem> {
|
||||
protected linkPath = 'workspaceitems';
|
||||
protected responseMsToLive = 10 * 1000;
|
||||
|
@@ -3,6 +3,7 @@ import { HttpClient } from '@angular/common/http';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
@@ -20,6 +21,7 @@ import { ProcessTaskResponse } from './models/process-task-response';
|
||||
* The service handling all REST requests for ClaimedTask
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(ClaimedTask)
|
||||
export class ClaimedTaskDataService extends TasksService<ClaimedTask> {
|
||||
|
||||
protected responseMsToLive = 10 * 1000;
|
||||
|
@@ -1,8 +1,10 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { link } from '../../cache/builders/build-decorators';
|
||||
|
||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||
import { DSpaceObject } from '../../shared/dspace-object.model';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { HALLink } from '../../shared/hal-link.model';
|
||||
import { WorkflowItem } from '../../submission/models/workflowitem.model';
|
||||
import { ResourceType } from '../../shared/resource-type';
|
||||
import { EPerson } from '../../eperson/models/eperson.model';
|
||||
@@ -32,15 +34,26 @@ export class TaskObject extends DSpaceObject implements CacheableObject {
|
||||
/**
|
||||
* The group of this task
|
||||
*/
|
||||
eperson: Observable<RemoteData<EPerson>>;
|
||||
@link(EPerson)
|
||||
eperson?: Observable<RemoteData<EPerson>>;
|
||||
|
||||
/**
|
||||
* The group of this task
|
||||
*/
|
||||
group: Observable<RemoteData<Group>>;
|
||||
@link(Group)
|
||||
group?: Observable<RemoteData<Group>>;
|
||||
|
||||
/**
|
||||
* The workflowitem object whom this task is related
|
||||
*/
|
||||
workflowitem: Observable<RemoteData<WorkflowItem>> | WorkflowItem;
|
||||
@link(WorkflowItem)
|
||||
workflowitem?: Observable<RemoteData<WorkflowItem>> | WorkflowItem;
|
||||
|
||||
_links: {
|
||||
self: HALLink,
|
||||
eperson: HALLink,
|
||||
group: HALLink,
|
||||
workflowitem: HALLink,
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ import { HttpClient } from '@angular/common/http';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { CoreState } from '../core.reducers';
|
||||
@@ -20,6 +21,7 @@ import { ProcessTaskResponse } from './models/process-task-response';
|
||||
* The service handling all REST requests for PoolTask
|
||||
*/
|
||||
@Injectable()
|
||||
@dataService(PoolTask)
|
||||
export class PoolTaskDataService extends TasksService<PoolTask> {
|
||||
|
||||
/**
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<div class="d-flex">
|
||||
<div class="person-thumbnail pr-2">
|
||||
<ds-thumbnail [thumbnail]="dso.getThumbnail() | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-thumbnail>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [defaultImage]="'assets/images/orgunit-placeholder.svg'"></ds-thumbnail>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<ds-org-unit-input-suggestions [suggestions]="allSuggestions" [(ngModel)]="selectedName" (clickSuggestion)="select($event)"
|
||||
|
@@ -1,4 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
|
||||
import { Bitstream } from '../../../../../core/shared/bitstream.model';
|
||||
import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators';
|
||||
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
||||
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
|
||||
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
@@ -37,6 +41,7 @@ export class OrgUnitSearchResultListSubmissionElementComponent extends SearchRes
|
||||
private translateService: TranslateService,
|
||||
private modalService: NgbModal,
|
||||
private itemDataService: ItemDataService,
|
||||
private bitstreamDataService: BitstreamDataService,
|
||||
private selectableListService: SelectableListService) {
|
||||
super(truncatableService);
|
||||
}
|
||||
@@ -95,4 +100,11 @@ export class OrgUnitSearchResultListSubmissionElementComponent extends SearchRes
|
||||
modalComp.value = value;
|
||||
return modalRef.result;
|
||||
}
|
||||
|
||||
// TODO refactor to return RemoteData, and thumbnail template to deal with loading
|
||||
getThumbnail(): Observable<Bitstream> {
|
||||
return this.bitstreamDataService.getThumbnailFor(this.dso).pipe(
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<div class="d-flex">
|
||||
<div class="person-thumbnail pr-2">
|
||||
<ds-thumbnail [thumbnail]="dso.getThumbnail() | async" [defaultImage]="'assets/images/person-placeholder.svg'"></ds-thumbnail>
|
||||
<ds-thumbnail [thumbnail]="getThumbnail() | async" [defaultImage]="'assets/images/person-placeholder.svg'"></ds-thumbnail>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<ds-person-input-suggestions [suggestions]="allSuggestions" [(ngModel)]="selectedName" (clickSuggestion)="select($event)" (submitSuggestion)="selectCustom($event)"></ds-person-input-suggestions>
|
||||
|
@@ -1,4 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service';
|
||||
import { Bitstream } from '../../../../../core/shared/bitstream.model';
|
||||
import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators';
|
||||
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
|
||||
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
|
||||
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
|
||||
@@ -37,6 +41,7 @@ export class PersonSearchResultListSubmissionElementComponent extends SearchResu
|
||||
private translateService: TranslateService,
|
||||
private modalService: NgbModal,
|
||||
private itemDataService: ItemDataService,
|
||||
private bitstreamDataService: BitstreamDataService,
|
||||
private selectableListService: SelectableListService) {
|
||||
super(truncatableService);
|
||||
}
|
||||
@@ -95,4 +100,11 @@ export class PersonSearchResultListSubmissionElementComponent extends SearchResu
|
||||
modalComp.value = value;
|
||||
return modalRef.result;
|
||||
}
|
||||
|
||||
// TODO refactor to return RemoteData, and thumbnail template to deal with loading
|
||||
getThumbnail(): Observable<Bitstream> {
|
||||
return this.bitstreamDataService.getThumbnailFor(this.dso).pipe(
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import {CUSTOM_ELEMENTS_SCHEMA, NgModule} from '@angular/core';
|
||||
import { QueryParamsDirectiveStub } from './query-params-directive-stub';
|
||||
import { MySimpleItemActionComponent } from '../../+item-page/edit-item-page/simple-item-action/abstract-simple-item-action.component.spec';
|
||||
// import { MySimpleItemActionComponent } from '../../+item-page/edit-item-page/simple-item-action/abstract-simple-item-action.component.spec';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {SharedModule} from '../shared.module';
|
||||
import { RouterLinkDirectiveStub } from './router-link-directive-stub';
|
||||
@@ -19,7 +19,7 @@ import { NgComponentOutletDirectiveStub } from './ng-component-outlet-directive-
|
||||
],
|
||||
declarations: [
|
||||
QueryParamsDirectiveStub,
|
||||
MySimpleItemActionComponent,
|
||||
// MySimpleItemActionComponent,
|
||||
RouterLinkDirectiveStub,
|
||||
NgComponentOutletDirectiveStub
|
||||
], schemas: [
|
||||
|
20
src/app/shared/utils/follow-link-config.model.ts
Normal file
20
src/app/shared/utils/follow-link-config.model.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { FindListOptions } from '../../core/data/request.models';
|
||||
import { HALResource } from '../../core/shared/hal-resource.model';
|
||||
|
||||
export class FollowLinkConfig<R extends HALResource> {
|
||||
name: keyof R['_links'];
|
||||
findListOptions?: FindListOptions;
|
||||
linksToFollow?: Array<FollowLinkConfig<any>>;
|
||||
}
|
||||
|
||||
export const followLink = <R extends HALResource>(
|
||||
linkName: keyof R['_links'],
|
||||
findListOptions?: FindListOptions,
|
||||
...linksToFollow: Array<FollowLinkConfig<any>>
|
||||
): FollowLinkConfig<R> => {
|
||||
return {
|
||||
name: linkName,
|
||||
findListOptions,
|
||||
linksToFollow
|
||||
}
|
||||
};
|
@@ -109,8 +109,8 @@ describe('SubmissionObjectEffects test suite', () => {
|
||||
const mappedActions = [];
|
||||
(submissionDefinitionResponse.sections as SubmissionSectionModel[])
|
||||
.forEach((sectionDefinition: SubmissionSectionModel) => {
|
||||
const sectionId = sectionDefinition._links.self.substr(sectionDefinition._links.self.lastIndexOf('/') + 1);
|
||||
const config = sectionDefinition._links.config || '';
|
||||
const sectionId = sectionDefinition._links.self.href.substr(sectionDefinition._links.self.href.lastIndexOf('/') + 1);
|
||||
const config = sectionDefinition._links.config.href || '';
|
||||
const enabled = (sectionDefinition.mandatory);
|
||||
const sectionData = {};
|
||||
const sectionErrors = null;
|
||||
|
@@ -57,8 +57,8 @@ export class SubmissionObjectEffects {
|
||||
const definition = action.payload.submissionDefinition;
|
||||
const mappedActions = [];
|
||||
definition.sections.page.forEach((sectionDefinition: SubmissionSectionModel) => {
|
||||
const sectionId = sectionDefinition._links.self.substr(sectionDefinition._links.self.lastIndexOf('/') + 1);
|
||||
const config = sectionDefinition._links.config || '';
|
||||
const sectionId = sectionDefinition._links.self.href.substr(sectionDefinition._links.self.href.lastIndexOf('/') + 1);
|
||||
const config = sectionDefinition._links.config.href || '';
|
||||
const enabled = (sectionDefinition.mandatory) || (isNotEmpty(action.payload.sections) && action.payload.sections.hasOwnProperty(sectionId));
|
||||
const sectionData = (isNotUndefined(action.payload.sections) && isNotUndefined(action.payload.sections[sectionId])) ? action.payload.sections[sectionId] : Object.create(null);
|
||||
const sectionErrors = null;
|
||||
|
@@ -134,7 +134,7 @@ export class SubmissionSectionLicenseComponent extends SectionModelComponent {
|
||||
|
||||
this.licenseText$ = this.collectionDataService.findById(this.collectionId).pipe(
|
||||
filter((collectionData: RemoteData<Collection>) => isNotUndefined((collectionData.payload))),
|
||||
flatMap((collectionData: RemoteData<Collection>) => collectionData.payload.license),
|
||||
flatMap((collectionData: RemoteData<Collection>) => (collectionData.payload as any).license),
|
||||
find((licenseData: RemoteData<License>) => isNotUndefined((licenseData.payload))),
|
||||
map((licenseData: RemoteData<License>) => licenseData.payload.text),
|
||||
startWith(''));
|
||||
|
@@ -2,6 +2,7 @@ import { ChangeDetectorRef, Component, Inject } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, Subscription} from 'rxjs';
|
||||
import { distinctUntilChanged, filter, find, flatMap, map, reduce, take, tap } from 'rxjs/operators';
|
||||
import { followLink } from '../../../shared/utils/follow-link-config.model';
|
||||
|
||||
import { SectionModelComponent } from '../models/section.model';
|
||||
import { hasValue, isNotEmpty, isNotUndefined, isUndefined } from '../../../shared/empty.util';
|
||||
@@ -160,13 +161,11 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
|
||||
filter((submissionObject: SubmissionObjectEntry) => isNotUndefined(submissionObject) && !submissionObject.isLoading),
|
||||
filter((submissionObject: SubmissionObjectEntry) => isUndefined(this.collectionId) || this.collectionId !== submissionObject.collection),
|
||||
tap((submissionObject: SubmissionObjectEntry) => this.collectionId = submissionObject.collection),
|
||||
flatMap((submissionObject: SubmissionObjectEntry) => this.collectionDataService.findById(submissionObject.collection)),
|
||||
flatMap((submissionObject: SubmissionObjectEntry) => this.collectionDataService.findById(submissionObject.collection, followLink('defaultAccessConditions'))),
|
||||
filter((rd: RemoteData<Collection>) => isNotUndefined((rd.payload))),
|
||||
tap((collectionRemoteData: RemoteData<Collection>) => this.collectionName = collectionRemoteData.payload.name),
|
||||
flatMap((collectionRemoteData: RemoteData<Collection>) => {
|
||||
return this.resourcePolicyService.findByHref(
|
||||
(collectionRemoteData.payload as any)._links.defaultAccessConditions
|
||||
);
|
||||
return this.resourcePolicyService.findByHref((collectionRemoteData.payload as any).defaultAccessConditions);
|
||||
}),
|
||||
filter((defaultAccessConditionsRemoteData: RemoteData<ResourcePolicy>) =>
|
||||
defaultAccessConditionsRemoteData.hasSucceeded),
|
||||
|
Reference in New Issue
Block a user