itermediate commit

This commit is contained in:
Art Lowel
2020-01-21 18:43:36 +01:00
parent 44facb8dcb
commit 56c3d12497
38 changed files with 228 additions and 268 deletions

View File

@@ -1,6 +1,7 @@
import { Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
import { distinctUntilChanged, filter, map, mergeMap, tap } from 'rxjs/operators';
import { Inject, Injectable } from '@angular/core';
import { EPersonDataService } from '../eperson/eperson-data.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { RequestService } from '../data/request.service';
import { GLOBAL_CONFIG } from '../../../config';

View File

@@ -8,6 +8,7 @@ import { distinctUntilChanged, filter, map, startWith, switchMap, take, withLate
import { RouterReducerState } from '@ngrx/router-store';
import { select, Store } from '@ngrx/store';
import { CookieAttributes } from 'js-cookie';
import { followLink } from '../../shared/utils/follow-link-config.model';
import { EPerson } from '../eperson/models/eperson.model';
import { AuthRequestService } from './auth-request.service';
@@ -133,7 +134,7 @@ export class AuthService {
headers = headers.append('Authorization', `Bearer ${token.accessToken}`);
options.headers = headers;
return this.authRequestService.getRequest('status', options).pipe(
map((status) => this.rdbService.build(status)),
map((status) => this.rdbService.build(status, followLink<AuthStatus>('eperson'))),
switchMap((status: AuthStatus) => {
if (status.authenticated) {
return status.eperson.pipe(map((eperson) => eperson.payload));

View File

@@ -1,3 +1,4 @@
import { link } from '../../cache/builders/build-decorators';
import { HALLink } from '../../shared/hal-link.model';
import { AuthError } from './auth-error.model';
import { AuthTokenInfo } from './auth-token-info.model';
@@ -41,6 +42,7 @@ export class AuthStatus implements CacheableObject {
/**
* The eperson of this auth status
*/
@link(EPerson)
eperson: Observable<RemoteData<EPerson>>;
/**
@@ -55,5 +57,6 @@ export class AuthStatus implements CacheableObject {
_links: {
self: HALLink;
eperson: HALLink
}
}

View File

@@ -35,7 +35,7 @@ export class NormalizedAuthStatus extends NormalizedObject<AuthStatus> {
/**
* The self link to the eperson of this auth status
*/
@relationship(EPerson, false)
@relationship(EPerson, false, false)
@autoserialize
eperson: string;
}

View File

@@ -137,7 +137,8 @@ export function getResolvedLinks(target: any) {
export class LinkDefinition<T extends HALResource> {
targetConstructor: GenericConstructor<CacheableObject>;
isList = false;
linkName?: keyof T['_links'];
linkName: keyof T['_links'];
propertyName: keyof T;
}
export const link = <T extends HALResource>(
@@ -145,7 +146,9 @@ export const link = <T extends HALResource>(
isList = false,
linkName?: keyof T['_links'],
) => {
return (target: T, key: string) => {
console.log('link call', targetConstructor, isList, linkName);
return (target: T, propertyName: string) => {
console.log('link return', targetConstructor, isList, linkName, target, propertyName);
let targetMap = linkMap.get(target.constructor);
if (hasNoValue(targetMap)) {
@@ -153,24 +156,25 @@ export const link = <T extends HALResource>(
}
if (hasNoValue(linkName)) {
linkName = key;
linkName = propertyName as any;
}
targetMap.set(key, {
targetMap.set(propertyName, {
targetConstructor,
isList,
linkName
linkName,
propertyName
});
linkMap.set(target.constructor, targetMap);
}
};
export const getLinks = <T extends HALResource>(source: GenericConstructor<T>): Map<keyof T['_links'], LinkDefinition<T>> => {
export const getLinkDefinitions = <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> => {
export const getLinkDefinition = <T extends HALResource>(source: GenericConstructor<T>, linkName: keyof T['_links']): LinkDefinition<T> => {
const sourceMap = linkMap.get(source);
if (hasValue(sourceMap)) {
return sourceMap.get(linkName);

View File

@@ -1,74 +0,0 @@
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;
}
}

View File

@@ -0,0 +1,57 @@
import { Injectable, Injector } from '@angular/core';
import { hasNoValue, isNotEmpty } from '../../../shared/empty.util';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { GenericConstructor } from '../../shared/generic-constructor';
import { HALResource } from '../../shared/hal-resource.model';
import { getDataServiceFor, getLinkDefinition, getLinkDefinitions, LinkDefinition } from './build-decorators';
@Injectable({
providedIn: 'root'
})
export class LinkService {
constructor(
protected parentInjector: Injector,
) {
}
public resolveLink<T extends HALResource>(model, linkToFollow: FollowLinkConfig<T>) {
const matchingLinkDef = getLinkDefinition(model.constructor, linkToFollow.name);
if (hasNoValue(matchingLinkDef)) {
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(matchingLinkDef.targetConstructor);
if (hasNoValue(provider)) {
throw new Error(`The @link() for ${linkToFollow.name} on ${model.constructor.name} models refers to a ${matchingLinkDef.targetConstructor.name}, but there is no service with an @dataService(${matchingLinkDef.targetConstructor.name}) annotation in order to retrieve it`);
}
const service = Injector.create({
providers: [],
parent: this.parentInjector
}).get(provider);
const href = model._links[matchingLinkDef.linkName].href;
if (matchingLinkDef.isList) {
model[linkToFollow.name] = service.findAllByHref(href, linkToFollow.findListOptions, ...linkToFollow.linksToFollow);
} else {
model[linkToFollow.name] = service.findByHref(href, ...linkToFollow.linksToFollow);
}
}
}
/**
* Remove any resolved links that the model may have.
*/
public removeResolvedLinks<T extends HALResource>(model: T) {
const linkDefs = getLinkDefinitions(model.constructor as GenericConstructor<T>);
if (isNotEmpty(linkDefs)) {
linkDefs.forEach((linkDef: LinkDefinition<T>) => {
model[linkDef.propertyName] = undefined;
});
}
}
}

View File

@@ -1,4 +1,4 @@
import { Injectable, Injector } from '@angular/core';
import { Injectable } from '@angular/core';
import {
combineLatest as observableCombineLatest,
@@ -6,7 +6,7 @@ import {
of as observableOf,
race as observableRace
} from 'rxjs';
import { distinctUntilChanged, flatMap, map, startWith, switchMap, tap } from 'rxjs/operators';
import { distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import {
hasNoValue,
@@ -16,6 +16,7 @@ import {
isNotEmpty,
isNotUndefined
} from '../../../shared/empty.util';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { PaginatedList } from '../../data/paginated-list';
import { RemoteData } from '../../data/remote-data';
@@ -23,31 +24,24 @@ 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 {
getDataServiceFor, getLink,
getLinks,
getMapsTo,
getRelationMetadata,
getRelationships
} from './build-decorators';
import { PageInfo } from '../../shared/page-info.model';
import {
filterSuccessfulResponses,
getRequestFromRequestHref,
getRequestFromRequestUUID,
getResourceLinksFromResponse
} from '../../shared/operators';
import { CacheableObject, TypedObject } from '../object-cache.reducer';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/testing/utils';
import { PageInfo } from '../../shared/page-info.model';
import { NormalizedObject } from '../models/normalized-object.model';
import { CacheableObject } from '../object-cache.reducer';
import { ObjectCacheService } from '../object-cache.service';
import { DSOSuccessResponse, ErrorResponse } from '../response.models';
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
import { LinkService } from './link.service';
@Injectable()
export class RemoteDataBuildService {
constructor(protected objectCache: ObjectCacheService,
private parentInjector: Injector,
protected linkService: LinkService,
protected requestService: RequestService) {
}
@@ -234,45 +228,12 @@ export class RemoteDataBuildService {
const domainModel = Object.assign(new domainModelConstructor(), normalized, halLinks);
linksToFollow.forEach((linkToFollow: FollowLinkConfig<T>) => {
this.resolveLink(domainModel, linkToFollow);
this.linkService.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[]>> {
if (isEmpty(input)) {

View File

@@ -23,14 +23,14 @@ export class NormalizedRelationship extends NormalizedObject<Relationship> {
* The item to the left of this relationship
*/
@deserialize
@relationship(Item, false)
@relationship(Item, false, false)
leftItem: string;
/**
* The item to the right of this relationship
*/
@deserialize
@relationship(Item, false)
@relationship(Item, false, false)
rightItem: string;
/**
@@ -61,7 +61,7 @@ export class NormalizedRelationship extends NormalizedObject<Relationship> {
* The type of Relationship
*/
@deserialize
@relationship(RelationshipType, false)
@relationship(RelationshipType, false, false)
relationshipType: string;
/**

View File

@@ -10,6 +10,7 @@ import { coreSelector } from '../core.selectors';
import { RestRequestMethod } from '../data/rest-request-method';
import { selfLinkFromUuidSelector } from '../index/index.selectors';
import { GenericConstructor } from '../shared/generic-constructor';
import { LinkService } from './builders/link.service';
import { NormalizedObject } from './models/normalized-object.model';
import {
AddPatchObjectCacheAction,
@@ -45,7 +46,10 @@ const entryFromSelfLinkSelector =
*/
@Injectable()
export class ObjectCacheService {
constructor(private store: Store<CoreState>) {
constructor(
private store: Store<CoreState>,
private linkService: LinkService
) {
}
/**
@@ -59,6 +63,7 @@ export class ObjectCacheService {
* The UUID of the request that resulted in this object
*/
add(objectToCache: CacheableObject, msToLive: number, requestUUID: string): void {
this.linkService.removeResolvedLinks(objectToCache); // Ensure the object we're storing has no resolved links
this.store.dispatch(new AddToObjectCacheAction(objectToCache, new Date().getTime(), msToLive, requestUUID));
}

View File

@@ -13,6 +13,7 @@ import { coreEffects } from './core.effects';
import { coreReducers } from './core.reducers';
import { isNotEmpty } from '../shared/empty.util';
import { EPersonDataService } from './eperson/eperson-data.service';
import { ApiService } from './services/api.service';
import { BrowseEntriesResponseParsingService } from './data/browse-entries-response-parsing.service';
@@ -25,14 +26,12 @@ import { DSpaceRESTv2Service } from './dspace-rest-v2/dspace-rest-v2.service';
import { FormBuilderService } from '../shared/form/builder/form-builder.service';
import { SectionFormOperationsService } from '../submission/sections/form/section-form-operations.service';
import { FormService } from '../shared/form/form.service';
import { GroupEpersonService } from './eperson/group-eperson.service';
import { HostWindowService } from '../shared/host-window.service';
import { ItemDataService } from './data/item-data.service';
import { MetadataService } from './metadata/metadata.service';
import { ObjectCacheService } from './cache/object-cache.service';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { RemoteDataBuildService } from './cache/builders/remote-data-build.service';
import { RequestService } from './data/request.service';
import { EndpointMapResponseParsingService } from './data/endpoint-map-response-parsing.service';
import { ServerResponseService } from './services/server-response.service';
import { NativeWindowFactory, NativeWindowService } from './services/window.service';
@@ -43,6 +42,8 @@ import { RouteService } from './services/route.service';
import { SubmissionDefinitionsConfigService } from './config/submission-definitions-config.service';
import { SubmissionFormsConfigService } from './config/submission-forms-config.service';
import { SubmissionSectionsConfigService } from './config/submission-sections-config.service';
import { Relationship } from './shared/item-relationships/relationship.model';
import { Item } from './shared/item.model';
import { SubmissionResponseParsingService } from './submission/submission-response-parsing.service';
import { EpersonResponseParsingService } from './eperson/eperson-response-parsing.service';
import { JsonPatchOperationsBuilder } from './json-patch/builder/json-patch-operations-builder';
@@ -183,7 +184,7 @@ const PROVIDERS = [
SectionFormOperationsService,
FormService,
EpersonResponseParsingService,
GroupEpersonService,
EPersonDataService,
HALEndpointService,
HostWindowService,
ItemDataService,
@@ -195,7 +196,6 @@ const PROVIDERS = [
BitstreamFormatDataService,
NormalizedObjectBuildService,
RemoteDataBuildService,
RequestService,
EndpointMapResponseParsingService,
FacetValueResponseParsingService,
FacetValueMapResponseParsingService,
@@ -273,6 +273,8 @@ const PROVIDERS = [
*/
export const normalizedModels =
[
Relationship,
Item,
NormalizedDSpaceObject,
NormalizedBundle,
NormalizedBitstream,
@@ -304,7 +306,7 @@ export const normalizedModels =
NormalizedRelationshipType,
NormalizedItemType,
NormalizedExternalSource,
NormalizedExternalSourceEntry
NormalizedExternalSourceEntry,
];
@NgModule({

View File

@@ -27,26 +27,14 @@ export abstract class BaseResponseParsingService {
return this.processArray(data, request);
} else if (isRestDataObject(data)) {
const object = this.deserialize(data);
// TODO remove
// if (isNotEmpty(data._embedded)) {
// Object
// .keys(data._embedded)
// .filter((property) => data._embedded.hasOwnProperty(property))
// .forEach((property) => {
// const parsedObj = this.process<ObjectDomain>(data._embedded[property], request);
// if (isNotEmpty(parsedObj)) {
// if (isRestPaginatedList(data._embedded[property])) {
// object[property] = parsedObj;
// object[property].page = parsedObj.page.map((obj) => this.retrieveObjectOrUrl(obj));
// } else if (isRestDataObject(data._embedded[property])) {
// object[property] = this.retrieveObjectOrUrl(parsedObj);
// } else if (Array.isArray(parsedObj)) {
// object[property] = parsedObj.map((obj) => this.retrieveObjectOrUrl(obj))
// }
// }
// });
// }
if (isNotEmpty(data._embedded)) {
Object
.keys(data._embedded)
.filter((property) => data._embedded.hasOwnProperty(property))
.forEach((property) => {
this.process<ObjectDomain>(data._embedded[property], request);
});
}
this.cache(object, request);
return object;

View File

@@ -49,11 +49,6 @@ export class BitstreamDataService extends DataService<Bitstream> {
super();
}
getBrowseEndpoint(options: FindListOptions, linkPath?: string): Observable<string> {
// TODO needed? if not, perhaps remove it from datasevice?
return undefined;
}
/**
* Retrieves the bitstreams in a given bundle
*

View File

@@ -13,7 +13,7 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { DeleteByIDRequest, FindListOptions, PostRequest, PutRequest } from './request.models';
import { DeleteByIDRequest, PostRequest, PutRequest } from './request.models';
import { Observable } from 'rxjs';
import { find, map, tap } from 'rxjs/operators';
import { configureRequest, getResponseFromEntry } from '../shared/operators';
@@ -59,16 +59,6 @@ export class BitstreamFormatDataService extends DataService<BitstreamFormat> {
super();
}
/**
* Get the endpoint for browsing bitstream formats
* @param {FindListOptions} options
* @param {string} linkPath
* @returns {Observable<string>}
*/
getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath);
}
/**
* Get the endpoint to update an existing bitstream format
* @param formatId

View File

@@ -14,7 +14,6 @@ import { CoreState } from '../core.reducers';
import { Bundle } from '../shared/bundle.model';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { Item } from '../shared/item.model';
import { BitstreamDataService } from './bitstream-data.service';
import { DataService } from './data.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { PaginatedList } from './paginated-list';
@@ -46,15 +45,6 @@ export class BundleDataService extends DataService<Bundle> {
super();
}
/**
* Get the endpoint for browsing bundles
* @param {FindListOptions} options
* @returns {Observable<string>}
*/
getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath);
}
findAllByItem(item: Item, options?: FindListOptions, ...linksToFollow: Array<FollowLinkConfig<Bundle>>): Observable<RemoteData<PaginatedList<Bundle>>> {
return this.findAllByHref(item._links.bundles.href, options, ...linksToFollow);
}

View File

@@ -65,7 +65,15 @@ export abstract class DataService<T extends CacheableObject> {
*/
protected responseMsToLive: number;
public abstract getBrowseEndpoint(options: FindListOptions, linkPath?: string): Observable<string>
/**
* Get the endpoint for browsing
* @param options The [[FindListOptions]] object
* @param linkPath The link path for the object
* @returns {Observable<string>}
*/
getBrowseEndpoint(options: FindListOptions = {}, linkPath?: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath);
}
/**
* Create the HREF with given options object

View File

@@ -40,10 +40,6 @@ export class DsoRedirectDataService extends DataService<any> {
super();
}
getBrowseEndpoint(options: FindListOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return this.halService.getEndpoint(linkPath);
}
setLinkPath(identifierType: IdentifierType) {
// The default 'pid' endpoint for identifiers does not support uuid lookups.
// For uuid lookups we need to change the linkPath.

View File

@@ -33,10 +33,6 @@ class DataServiceImpl extends DataService<DSpaceObject> {
super();
}
getBrowseEndpoint(options: FindListOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return this.halService.getEndpoint(linkPath);
}
getIDHref(endpoint, resourceID): string {
return endpoint.replace(/\{\?uuid\}/,`?uuid=${resourceID}`);
}

View File

@@ -34,9 +34,6 @@ class DataServiceImpl extends DataService<MetadataSchema> {
super();
}
getBrowseEndpoint(options: FindListOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return this.halService.getEndpoint(linkPath);
}
}
/**

View File

@@ -1,4 +1,15 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '../../app.reducer';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { CoreState } from '../core.reducers';
import { DataService } from './data.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { ItemDataService } from './item-data.service';
import { RequestService } from './request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
@@ -14,15 +25,25 @@ import { isNotUndefined } from '../../shared/empty.util';
import { FindListOptions, FindListRequest } from './request.models';
/**
* The service handling all relationship requests
* The service handling all relationship type requests
*/
@Injectable()
export class RelationshipTypeService {
@dataService(RelationshipType)
export class RelationshipTypeService extends DataService<RelationshipType> {
protected linkPath = 'relationshiptypes';
constructor(protected requestService: RequestService,
constructor(protected itemService: ItemDataService,
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected dataBuildService: NormalizedObjectBuildService,
protected store: Store<CoreState>,
protected halService: HALEndpointService,
protected rdbService: RemoteDataBuildService) {
protected objectCache: ObjectCacheService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DefaultChangeAnalyzer<RelationshipType>,
protected appStore: Store<AppState>) {
super()
}
/**

View File

@@ -1,5 +1,6 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { followLink } from '../../shared/utils/follow-link-config.model';
import { dataService } from '../cache/builders/build-decorators';
import { MemoizedSelector, select, Store } from '@ngrx/store';
import { combineLatest, combineLatest as observableCombineLatest } from 'rxjs';
@@ -64,10 +65,6 @@ export class RelationshipService extends DataService<Relationship> {
super();
}
getBrowseEndpoint(options: FindListOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return this.halService.getEndpoint(linkPath);
}
/**
* Get the endpoint for a relationship by ID
* @param uuid
@@ -249,7 +246,7 @@ export class RelationshipService extends DataService<Relationship> {
} else {
findListOptions.searchParams = searchParams;
}
return this.searchBy('byLabel', findListOptions);
return this.searchBy('byLabel', findListOptions, followLink('leftItem'), followLink('rightItem'), followLink('relationshipType'));
}
/**

View File

@@ -71,7 +71,9 @@ const getUuidsFromHrefSubstring = (state: IndexState, href: string): string[] =>
/**
* A service to interact with the request state in the store
*/
@Injectable()
@Injectable({
providedIn: 'root'
})
export class RequestService {
private requestsOnTheirWayToTheStore: string[] = [];

View File

@@ -37,9 +37,6 @@ class DataServiceImpl extends DataService<ResourcePolicy> {
super();
}
getBrowseEndpoint(options: FindListOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return this.halService.getEndpoint(linkPath);
}
}
/**

View File

@@ -11,7 +11,6 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { HttpClient } from '@angular/common/http';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { FindListOptions } from './request.models';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { RemoteData } from './remote-data';
@@ -26,7 +25,6 @@ import { getSucceededRemoteData } from '../shared/operators';
@dataService(Site)
export class SiteDataService extends DataService<Site> {
protected linkPath = 'sites';
protected forceBypassCache = false;
constructor(
protected requestService: RequestService,
@@ -42,15 +40,6 @@ export class SiteDataService extends DataService<Site> {
super();
}
/**
* Get the endpoint for browsing the site object
* @param {FindListOptions} options
* @param {Observable<string>} linkPath
*/
getBrowseEndpoint(options: FindListOptions, linkPath?: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath);
}
/**
* Retrieve the Site Object
*/

View File

@@ -0,0 +1,36 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { NotificationsService } from '../../shared/notifications/notifications.service';
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';
import { CoreState } from '../core.reducers';
import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
import { DataService } from '../data/data.service';
import { RequestService } from '../data/request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { EPerson } from './models/eperson.model';
@Injectable()
@dataService(EPerson)
export class EPersonDataService extends DataService<EPerson> {
protected linkPath: 'eperson/epersons';
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected dataBuildService: NormalizedObjectBuildService,
protected store: Store<CoreState>,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected http: HttpClient,
protected comparator: DSOChangeAnalyzer<EPerson>
) {
super();
}
}

View File

@@ -1,14 +0,0 @@
import { Observable } from 'rxjs';
import { FindListOptions } from '../data/request.models';
import { DataService } from '../data/data.service';
import { CacheableObject } from '../cache/object-cache.reducer';
/**
* An abstract class that provides methods to make HTTP request to eperson endpoint.
*/
export abstract class EpersonService<TDomain extends CacheableObject> extends DataService<TDomain> {
public getBrowseEndpoint(options: FindListOptions): Observable<string> {
return this.halService.getEndpoint(this.linkPath);
}
}

View File

@@ -4,8 +4,9 @@ import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { filter, map, take } from 'rxjs/operators';
import { DataService } from '../data/data.service';
import { EpersonService } from './eperson.service';
import { EPersonDataService } from './eperson-data.service';
import { RequestService } from '../data/request.service';
import { FindListOptions } from '../data/request.models';
import { HALEndpointService } from '../shared/hal-endpoint.service';
@@ -23,9 +24,11 @@ import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service';
/**
* Provides methods to retrieve eperson group resources.
*/
@Injectable()
export class GroupEpersonService extends EpersonService<Group> {
protected linkPath = 'groups';
@Injectable({
providedIn: 'root'
})
export class GroupDataService extends DataService<Group> {
protected linkPath = 'eperson/groups';
protected browseEndpoint = '';
constructor(

View File

@@ -1,3 +1,4 @@
import { HALLink } from './hal-link.model';
import { MetadataMap } from './metadata.models';
import { ResourceType } from './resource-type';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
@@ -39,6 +40,10 @@ export class ExternalSourceEntry extends ListableObject {
*/
self: string;
_links: {
self: HALLink;
};
/**
* Method that returns as which type of object this object should be rendered
*/

View File

@@ -1,3 +1,4 @@
import { HALLink } from './hal-link.model';
import { ResourceType } from './resource-type';
import { CacheableObject } from '../cache/object-cache.reducer';
@@ -26,4 +27,8 @@ export class ExternalSource extends CacheableObject {
* The link to the rest endpoint where this External Source can be found
*/
self: string;
_links: {
self: HALLink;
}
}

View File

@@ -31,6 +31,8 @@ export class Relationship implements CacheableObject {
/**
* The item to the left of this relationship
*/
// TODO it's likely a circular dependency 😒 -> https://stackoverflow.com/questions/35240716/webpack-import-returns-undefined-depending-on-the-order-of-imports
@link(Item)
leftItem?: Observable<RemoteData<Item>>;

View File

@@ -6,6 +6,7 @@ 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 { Collection } from './collection.model';
import { DSpaceObject } from './dspace-object.model';
import { GenericConstructor } from './generic-constructor';
@@ -44,6 +45,12 @@ export class Item extends DSpaceObject {
*/
isWithdrawn: boolean;
/**
* The Collection that owns this Item
*/
@link(Collection)
owningCollection: Observable<RemoteData<Collection>>;
@link(Bundle, true)
bundles: Observable<RemoteData<PaginatedList<Bundle>>>;

View File

@@ -3,6 +3,7 @@ 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 { LinkService } from '../../cache/builders/link.service';
import { FacetConfigSuccessResponse, FacetValueSuccessResponse, SearchSuccessResponse } from '../../cache/response.models';
import { PaginatedList } from '../../data/paginated-list';
import { ResponseParsingService } from '../../data/parsing.service';
@@ -70,6 +71,7 @@ export class SearchService implements OnDestroy {
private routeService: RouteService,
protected requestService: RequestService,
private rdb: RemoteDataBuildService,
private linkService: LinkService,
private halService: HALEndpointService,
private communityService: CommunityDataService,
private dspaceObjectService: DSpaceObjectDataService
@@ -341,8 +343,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'));
this.linkService.resolveLink(community, followLink('subcommunities'));
this.linkService.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*/

View File

@@ -37,8 +37,4 @@ export class WorkflowItemDataService extends DataService<WorkflowItem> {
super();
}
public getBrowseEndpoint(options: FindListOptions) {
return this.halService.getEndpoint(this.linkPath);
}
}

View File

@@ -37,8 +37,4 @@ export class WorkspaceitemDataService extends DataService<WorkspaceItem> {
super();
}
public getBrowseEndpoint(options: FindListOptions) {
return this.halService.getEndpoint(this.linkPath);
}
}

View File

@@ -18,10 +18,6 @@ import { CacheableObject } from '../cache/object-cache.reducer';
*/
export abstract class TasksService<T extends CacheableObject> extends DataService<T> {
public getBrowseEndpoint(options: FindListOptions): Observable<string> {
return this.halService.getEndpoint(this.linkPath);
}
/**
* Fetch a RestRequest
*

View File

@@ -2,7 +2,7 @@ import { Component, Input, OnInit } from '@angular/core';
import { find } from 'rxjs/operators';
import { GroupEpersonService } from '../../../../core/eperson/group-eperson.service';
import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { ResourcePolicy } from '../../../../core/shared/resource-policy.model';
import { isEmpty } from '../../../../shared/empty.util';
import { Group } from '../../../../core/eperson/models/group.model';
@@ -32,9 +32,9 @@ export class SubmissionSectionUploadAccessConditionsComponent implements OnInit
/**
* Initialize instance variables
*
* @param {GroupEpersonService} groupService
* @param {GroupDataService} groupService
*/
constructor(private groupService: GroupEpersonService) {}
constructor(private groupService: GroupDataService) {}
/**
* Retrieve access conditions list

View File

@@ -27,7 +27,7 @@ import { SubmissionUploadsConfigService } from '../../../core/config/submission-
import { SectionUploadService } from './section-upload.service';
import { SubmissionSectionUploadComponent } from './section-upload.component';
import { CollectionDataService } from '../../../core/data/collection-data.service';
import { GroupEpersonService } from '../../../core/eperson/group-eperson.service';
import { GroupDataService } from '../../../core/eperson/group-data.service';
import { cold, hot } from 'jasmine-marbles';
import { Collection } from '../../../core/shared/collection.model';
import { ResourcePolicy } from '../../../core/shared/resource-policy.model';
@@ -52,8 +52,8 @@ function getMockCollectionDataService(): CollectionDataService {
});
}
function getMockGroupEpersonService(): GroupEpersonService {
return jasmine.createSpyObj('GroupEpersonService', {
function getMockGroupEpersonService(): GroupDataService {
return jasmine.createSpyObj('GroupDataService', {
findById: jasmine.createSpy('findById'),
});
@@ -134,7 +134,7 @@ describe('SubmissionSectionUploadComponent test suite', () => {
],
providers: [
{ provide: CollectionDataService, useValue: getMockCollectionDataService() },
{ provide: GroupEpersonService, useValue: getMockGroupEpersonService() },
{ provide: GroupDataService, useValue: getMockGroupEpersonService() },
{ provide: ResourcePolicyService, useValue: getMockResourcePolicyService() },
{ provide: SubmissionUploadsConfigService, useValue: getMockSubmissionUploadsConfigService() },
{ provide: SectionsService, useClass: SectionsServiceStub },
@@ -181,7 +181,7 @@ describe('SubmissionSectionUploadComponent test suite', () => {
submissionServiceStub = TestBed.get(SubmissionService);
sectionsServiceStub = TestBed.get(SectionsService);
collectionDataService = TestBed.get(CollectionDataService);
groupService = TestBed.get(GroupEpersonService);
groupService = TestBed.get(GroupDataService);
resourcePolicyService = TestBed.get(ResourcePolicyService);
uploadsConfigService = TestBed.get(SubmissionUploadsConfigService);
bitstreamService = TestBed.get(SectionUploadService);

View File

@@ -8,7 +8,7 @@ import { SectionModelComponent } from '../models/section.model';
import { hasValue, isNotEmpty, isNotUndefined, isUndefined } from '../../../shared/empty.util';
import { SectionUploadService } from './section-upload.service';
import { CollectionDataService } from '../../../core/data/collection-data.service';
import { GroupEpersonService } from '../../../core/eperson/group-eperson.service';
import { GroupDataService } from '../../../core/eperson/group-data.service';
import { ResourcePolicyService } from '../../../core/data/resource-policy.service';
import { SubmissionUploadsConfigService } from '../../../core/config/submission-uploads-config.service';
import { SubmissionUploadsModel } from '../../../core/config/models/config-submission-uploads.model';
@@ -123,7 +123,7 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
* @param {SectionUploadService} bitstreamService
* @param {ChangeDetectorRef} changeDetectorRef
* @param {CollectionDataService} collectionDataService
* @param {GroupEpersonService} groupService
* @param {GroupDataService} groupService
* @param {ResourcePolicyService} resourcePolicyService
* @param {SectionsService} sectionService
* @param {SubmissionService} submissionService
@@ -134,7 +134,7 @@ export class SubmissionSectionUploadComponent extends SectionModelComponent {
constructor(private bitstreamService: SectionUploadService,
private changeDetectorRef: ChangeDetectorRef,
private collectionDataService: CollectionDataService,
private groupService: GroupEpersonService,
private groupService: GroupDataService,
private resourcePolicyService: ResourcePolicyService,
protected sectionService: SectionsService,
private submissionService: SubmissionService,