diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index 03f6e50444..f8b4694a89 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -185,7 +185,12 @@ export class RemoteDataBuildService { rdArr.push(this.buildSingle(href, resourceConstructor)); }); - links[relationship] = this.aggregate(rdArr); + if (rdArr.length === 1) { + links[relationship] = rdArr[0]; + } + else { + links[relationship] = this.aggregate(rdArr); + } } else { // without the setTimeout, the actions inside requestService.configure diff --git a/src/app/core/cache/models/normalized-item.model.ts b/src/app/core/cache/models/normalized-item.model.ts index 6ed2ede8c2..a4e52110ff 100644 --- a/src/app/core/cache/models/normalized-item.model.ts +++ b/src/app/core/cache/models/normalized-item.model.ts @@ -48,9 +48,8 @@ export class NormalizedItem extends NormalizedDSpaceObject { /** * The Collection that owns this Item */ - @autoserializeAs(String, 'owningCollection') @relationship(ResourceType.Collection, false) - owner: string; + owningCollection: string; @autoserialize @relationship(ResourceType.Bitstream, true) diff --git a/src/app/core/data/remote-data.ts b/src/app/core/data/remote-data.ts index 9c6cda96bd..7f0cf06979 100644 --- a/src/app/core/data/remote-data.ts +++ b/src/app/core/data/remote-data.ts @@ -2,10 +2,10 @@ import { Observable } from "rxjs"; import { PageInfo } from "../shared/page-info.model"; export enum RemoteDataState { - RequestPending, - ResponsePending, - Failed, - Success + RequestPending = "RequestPending", + ResponsePending = "ResponsePending", + Failed = "Failed", + Success = "Success" } /** diff --git a/src/app/core/data/request.effects.ts b/src/app/core/data/request.effects.ts index 1fb325bb3c..dcfa4df66f 100644 --- a/src/app/core/data/request.effects.ts +++ b/src/app/core/data/request.effects.ts @@ -20,6 +20,7 @@ import { NormalizedObjectFactory } from "../cache/models/normalized-object-facto import { ResourceType } from "../shared/resource-type"; import { RequestError } from "./request.models"; import { PageInfo } from "../shared/page-info.model"; +import { NormalizedObject } from "../cache/models/normalized-object.model"; function isObjectLevel(halObj: any) { return isNotEmpty(halObj._links) && hasValue(halObj._links.self); @@ -29,6 +30,19 @@ function isPaginatedResponse(halObj: any) { return isNotEmpty(halObj.page) && hasValue(halObj._embedded); } +function flattenSingleKeyObject(obj: any): any { + const keys = Object.keys(obj); + if (keys.length !== 1) { + throw new Error(`Expected an object with a single key, got: ${JSON.stringify(obj)}`); + } + return obj[keys[0]]; +} + +class ProcessRequestDTO { + [key: string]: NormalizedObject[] +} + + @Injectable() export class RequestEffects { @@ -49,85 +63,90 @@ export class RequestEffects { }) .flatMap((entry: RequestEntry) => { return this.restApi.get(entry.request.href) - .map((data: DSpaceRESTV2Response) => new SuccessResponse(this.process(data.payload, entry.request.href), data.statusCode, this.processPageInfo(data.payload.page))) - .do((response: Response) => this.responseCache.add(entry.request.href, response, this.EnvConfig.cache.msToLive)) + .map((data: DSpaceRESTV2Response) => { + const processRequestDTO = this.process(data.payload, entry.request.href); + const uuids = flattenSingleKeyObject(processRequestDTO).map(no => no.uuid); + return new SuccessResponse(uuids, data.statusCode, this.processPageInfo(data.payload.page)) + }).do((response: Response) => this.responseCache.add(entry.request.href, response, this.EnvConfig.cache.msToLive)) .map((response: Response) => new RequestCompleteAction(entry.request.href)) .catch((error: RequestError) => Observable.of(new ErrorResponse(error)) .do((response: Response) => this.responseCache.add(entry.request.href, response, this.EnvConfig.cache.msToLive)) .map((response: Response) => new RequestCompleteAction(entry.request.href))); }); - protected process(data: any, requestHref: string): Array { + protected process(data: any, requestHref: string): ProcessRequestDTO { if (isNotEmpty(data)) { if (isPaginatedResponse(data)) { return this.process(data._embedded, requestHref); } else if (isObjectLevel(data)) { - return this.deserializeAndCache(data, requestHref); + return { "topLevel": this.deserializeAndCache(data, requestHref) }; } else { - let uuids = []; - Object.keys(data) - .filter(property => data.hasOwnProperty(property)) - .filter(property => hasValue(data[property])) - .forEach(property => { - let propertyUUIDs; - - if (isPaginatedResponse(data[property])) { - propertyUUIDs = this.process(data[property], requestHref); + let result = new ProcessRequestDTO(); + if (Array.isArray(data)) { + result['topLevel'] = []; + data.forEach(datum => { + if (isPaginatedResponse(datum)) { + const obj = this.process(datum, requestHref); + result['topLevel'] = [...result['topLevel'], ...flattenSingleKeyObject(obj)]; } else { - propertyUUIDs = this.deserializeAndCache(data[property], requestHref); + result['topLevel'] = [...result['topLevel'], ...this.deserializeAndCache(datum, requestHref)]; } - - uuids = [...uuids, ...propertyUUIDs]; }); - return uuids; + } + else { + Object.keys(data) + .filter(property => data.hasOwnProperty(property)) + .filter(property => hasValue(data[property])) + .forEach(property => { + if (isPaginatedResponse(data[property])) { + const obj = this.process(data[property], requestHref); + result[property] = flattenSingleKeyObject(obj); + } + else { + result[property] = this.deserializeAndCache(data[property], requestHref); + } + }); + } + return result; } } } - protected deserializeAndCache(obj, requestHref: string): Array { - let type: ResourceType; - const isArray = Array.isArray(obj); - - if (isArray && isEmpty(obj)) { - return []; - } - - if (isArray) { - type = obj[0]["type"]; - } - else { - type = obj["type"]; + protected deserializeAndCache(obj, requestHref: string): NormalizedObject[] { + if(Array.isArray(obj)) { + let result = []; + obj.forEach(o => result = [...result, ...this.deserializeAndCache(o, requestHref)]) + return result; } + let type: ResourceType = obj["type"]; if (hasValue(type)) { const normObjConstructor = NormalizedObjectFactory.getConstructor(type); if (hasValue(normObjConstructor)) { const serializer = new DSpaceRESTv2Serializer(normObjConstructor); - if (isArray) { - obj.forEach(o => { - if (isNotEmpty(o._embedded)) { - this.process(o._embedded, requestHref); - } + let processed; + if (isNotEmpty(obj._embedded)) { + processed = this.process(obj._embedded, requestHref); + } + let normalizedObj = serializer.deserialize(obj); + + if (isNotEmpty(processed)) { + let linksOnly = {}; + Object.keys(processed).forEach(key => { + linksOnly[key] = processed[key].map((no: NormalizedObject) => no.self); }); - const normalizedObjArr = serializer.deserializeArray(obj); - normalizedObjArr.forEach(t => this.addToObjectCache(t, requestHref)); - return normalizedObjArr.map(t => t.uuid); - } - else { - if (isNotEmpty(obj._embedded)) { - this.process(obj._embedded, requestHref); - } - const normalizedObj = serializer.deserialize(obj); - this.addToObjectCache(normalizedObj, requestHref); - return [normalizedObj.uuid]; + Object.assign(normalizedObj, linksOnly); } + this.addToObjectCache(normalizedObj, requestHref); + return [normalizedObj]; + } else { //TODO move check to Validator? diff --git a/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts b/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts index 9107d51bb7..400a5c851e 100644 --- a/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts +++ b/src/app/core/dspace-rest-v2/dspace-rest-v2.service.ts @@ -23,7 +23,7 @@ export class DSpaceRESTv2Service { * @param options * A RequestOptionsArgs object, with options for the http call. * @return {Observable} - * An Observablse containing the response from the server + * An Observable containing the response from the server */ get(absoluteURL: string, options?: RequestOptionsArgs): Observable { return this.http.get(absoluteURL, options) diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index cb488ab0a8..e57c6aec4e 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -40,7 +40,11 @@ export class Item extends DSpaceObject { /** * The Collection that owns this Item */ - owner: RemoteData; + owningCollection: RemoteData; + + get owner(): RemoteData { + return this.owningCollection; + } bitstreams: RemoteData;