diff --git a/package.json b/package.json index 1a3ac750b7..88476a91cf 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "morgan": "1.7.0", "ng2-translate": "4.2.0", "preboot": "4.5.2", + "reflect-metadata": "^0.1.10", "rxjs": "5.0.0-beta.12", "ts-md5": "^1.2.0", "webfontloader": "1.6.27", @@ -159,7 +160,6 @@ "protractor": "~4.0.14", "protractor-istanbul-plugin": "~2.0.0", "raw-loader": "0.5.1", - "reflect-metadata": "0.1.8", "rimraf": "2.5.4", "rollup": "0.37.0", "rollup-plugin-commonjs": "6.0.0", diff --git a/src/app/core/cache/builders/build-decorators.ts b/src/app/core/cache/builders/build-decorators.ts index bb86ad4ae1..00cb50663a 100644 --- a/src/app/core/cache/builders/build-decorators.ts +++ b/src/app/core/cache/builders/build-decorators.ts @@ -1,5 +1,7 @@ +import "reflect-metadata"; import { GenericConstructor } from "../../shared/generic-constructor"; import { CacheableObject } from "../object-cache.reducer"; +import { NormalizedDSOType } from "../models/normalized-dspace-object-type"; const mapsToMetadataKey = Symbol("mapsTo"); const relationshipKey = Symbol("relationship"); @@ -14,7 +16,7 @@ export const getMapsTo = function(target: any) { return Reflect.getOwnMetadata(mapsToMetadataKey, target); }; -export const relationship = function(value: any): any { +export const relationship = function(value: NormalizedDSOType): any { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { if (!target || !propertyKey) { return; 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 511c5d594a..f37f0ce4f9 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -13,6 +13,7 @@ import { Observable } from "rxjs/Observable"; import { RemoteData } from "../../data/remote-data"; import { GenericConstructor } from "../../shared/generic-constructor"; import { getMapsTo, getResourceType, getRelationships } from "./build-decorators"; +import { NormalizedDSOFactory } from "../models/normalized-dspace-object-factory"; @Injectable() export class RemoteDataBuildService { @@ -45,13 +46,13 @@ export class RemoteDataBuildService { const payload = Observable.race( - this.objectCache.getBySelfLink(href), + this.objectCache.getBySelfLink(href, normalizedType), responseCacheObs .filter((entry: ResponseCacheEntry) => hasValue(entry) && entry.response.isSuccessful) .map((entry: ResponseCacheEntry) => ( entry.response).resourceUUIDs) .flatMap((resourceUUIDs: Array) => { if (isNotEmpty(resourceUUIDs)) { - return this.objectCache.get(resourceUUIDs[0]); + return this.objectCache.get(resourceUUIDs[0], normalizedType); } else { return Observable.of(undefined); @@ -95,7 +96,7 @@ export class RemoteDataBuildService { .filter((entry: ResponseCacheEntry) => hasValue(entry) && entry.response.isSuccessful) .map((entry: ResponseCacheEntry) => ( entry.response).resourceUUIDs) .flatMap((resourceUUIDs: Array) => { - return this.objectCache.getList(resourceUUIDs) + return this.objectCache.getList(resourceUUIDs, normalizedType) .map((normList: TNormalized[]) => { return normList.map((normalized: TNormalized) => { return this.build(normalized); @@ -123,32 +124,33 @@ export class RemoteDataBuildService { relationships.forEach((relationship: string) => { if (hasValue(normalized[relationship])) { const resourceType = getResourceType(normalized, relationship); + const resourceConstructor = NormalizedDSOFactory.getConstructor(resourceType); if (Array.isArray(normalized[relationship])) { // without the setTimeout, the actions inside requestService.configure // are dispatched, but sometimes don't arrive. I'm unsure why atm. setTimeout(() => { normalized[relationship].forEach((href: string) => { - this.requestService.configure(href, resourceType) + this.requestService.configure(href, resourceConstructor) }); }, 0); links[relationship] = normalized[relationship].map((href: string) => { - return this.buildSingle(href, resourceType); + return this.buildSingle(href, resourceConstructor); }); } else { // without the setTimeout, the actions inside requestService.configure // are dispatched, but sometimes don't arrive. I'm unsure why atm. setTimeout(() => { - this.requestService.configure(normalized[relationship], resourceType); + this.requestService.configure(normalized[relationship], resourceConstructor); },0); - links[relationship] = this.buildSingle(normalized[relationship], resourceType); + links[relationship] = this.buildSingle(normalized[relationship], resourceConstructor); } } }); - const constructor = getMapsTo(normalized.constructor); - return Object.assign(new constructor(), normalized, links); + const domainModel = getMapsTo(normalized.constructor); + return Object.assign(new domainModel(), normalized, links); } } diff --git a/src/app/core/cache/models/normalized-bundle.model.ts b/src/app/core/cache/models/normalized-bundle.model.ts index 834fd770f6..bb0e7b0708 100644 --- a/src/app/core/cache/models/normalized-bundle.model.ts +++ b/src/app/core/cache/models/normalized-bundle.model.ts @@ -2,7 +2,7 @@ import { autoserialize, inheritSerialization } from "cerialize"; import { NormalizedDSpaceObject } from "./normalized-dspace-object.model"; import { Bundle } from "../../shared/bundle.model"; import { mapsTo, relationship } from "../builders/build-decorators"; -import { NormalizedBitstream } from "./normalized-bitstream.model"; +import { NormalizedDSOType } from "./normalized-dspace-object-type"; @mapsTo(Bundle) @inheritSerialization(NormalizedDSpaceObject) @@ -11,7 +11,7 @@ export class NormalizedBundle extends NormalizedDSpaceObject { * The primary bitstream of this Bundle */ @autoserialize - @relationship(NormalizedBitstream) + @relationship(NormalizedDSOType.NormalizedBitstream) primaryBitstream: string; /** @@ -25,6 +25,6 @@ export class NormalizedBundle extends NormalizedDSpaceObject { owner: string; @autoserialize - @relationship(NormalizedBitstream) + @relationship(NormalizedDSOType.NormalizedBitstream) bitstreams: Array; } diff --git a/src/app/core/cache/models/normalized-collection.model.ts b/src/app/core/cache/models/normalized-collection.model.ts index 0584646ccb..9b31f34837 100644 --- a/src/app/core/cache/models/normalized-collection.model.ts +++ b/src/app/core/cache/models/normalized-collection.model.ts @@ -2,7 +2,7 @@ import { autoserialize, inheritSerialization, autoserializeAs } from "cerialize" import { NormalizedDSpaceObject } from "./normalized-dspace-object.model"; import { Collection } from "../../shared/collection.model"; import { mapsTo, relationship } from "../builders/build-decorators"; -import { NormalizedItem } from "./normalized-item.model"; +import { NormalizedDSOType } from "./normalized-dspace-object-type"; @mapsTo(Collection) @inheritSerialization(NormalizedDSpaceObject) @@ -30,7 +30,7 @@ export class NormalizedCollection extends NormalizedDSpaceObject { owner: string; @autoserialize - @relationship(NormalizedItem) + @relationship(NormalizedDSOType.NormalizedItem) items: Array; } diff --git a/src/app/core/cache/models/normalized-dspace-object-factory.ts b/src/app/core/cache/models/normalized-dspace-object-factory.ts new file mode 100644 index 0000000000..18ce1de660 --- /dev/null +++ b/src/app/core/cache/models/normalized-dspace-object-factory.ts @@ -0,0 +1,29 @@ +import { NormalizedDSpaceObject } from "./normalized-dspace-object.model"; +import { NormalizedBitstream } from "./normalized-bitstream.model"; +import { NormalizedBundle } from "./normalized-bundle.model"; +import { NormalizedItem } from "./normalized-item.model"; +import { NormalizedCollection } from "./normalized-collection.model"; +import { GenericConstructor } from "../../shared/generic-constructor"; +import { NormalizedDSOType } from "./normalized-dspace-object-type"; + +export class NormalizedDSOFactory { + public static getConstructor(type: NormalizedDSOType): GenericConstructor { + switch (type) { + case NormalizedDSOType.NormalizedBitstream: { + return NormalizedBitstream + } + case NormalizedDSOType.NormalizedBundle: { + return NormalizedBundle + } + case NormalizedDSOType.NormalizedItem: { + return NormalizedItem + } + case NormalizedDSOType.NormalizedCollection: { + return NormalizedCollection + } + default: { + return undefined; + } + } + } +} diff --git a/src/app/core/cache/models/normalized-dspace-object-type.ts b/src/app/core/cache/models/normalized-dspace-object-type.ts new file mode 100644 index 0000000000..361989d2b6 --- /dev/null +++ b/src/app/core/cache/models/normalized-dspace-object-type.ts @@ -0,0 +1,6 @@ +export enum NormalizedDSOType { + NormalizedBitstream, + NormalizedBundle, + NormalizedItem, + NormalizedCollection +} diff --git a/src/app/core/cache/models/normalized-item.model.ts b/src/app/core/cache/models/normalized-item.model.ts index 74a3063a20..cdd3acdb92 100644 --- a/src/app/core/cache/models/normalized-item.model.ts +++ b/src/app/core/cache/models/normalized-item.model.ts @@ -2,8 +2,7 @@ import { inheritSerialization, autoserialize } from "cerialize"; import { NormalizedDSpaceObject } from "./normalized-dspace-object.model"; import { Item } from "../../shared/item.model"; import { mapsTo, relationship } from "../builders/build-decorators"; -import { NormalizedBundle } from "./normalized-bundle.model"; -import { NormalizedCollection } from "./normalized-collection.model"; +import { NormalizedDSOType } from "./normalized-dspace-object-type"; @mapsTo(Item) @inheritSerialization(NormalizedDSpaceObject) @@ -34,7 +33,7 @@ export class NormalizedItem extends NormalizedDSpaceObject { * An array of Collections that are direct parents of this Item */ @autoserialize - @relationship(NormalizedCollection) + @relationship(NormalizedDSOType.NormalizedCollection) parents: Array; /** @@ -43,6 +42,6 @@ export class NormalizedItem extends NormalizedDSpaceObject { owner: string; @autoserialize - @relationship(NormalizedBundle) + @relationship(NormalizedDSOType.NormalizedBundle) bundles: Array; } diff --git a/src/app/core/cache/object-cache.service.spec.ts b/src/app/core/cache/object-cache.service.spec.ts index d380829452..b5e40f37de 100644 --- a/src/app/core/cache/object-cache.service.spec.ts +++ b/src/app/core/cache/object-cache.service.spec.ts @@ -65,8 +65,6 @@ describe("ObjectCacheService", () => { service.get(uuid).take(1).subscribe(o => testObj = o); expect(testObj.uuid).toBe(uuid); expect(testObj.foo).toBe("bar"); - // this only works if testObj is an instance of TestClass - expect(testObj.test()).toBe("bar" + uuid); }); it("should not return a cached object that has exceeded its time to live", () => { @@ -87,10 +85,8 @@ describe("ObjectCacheService", () => { service.getList([uuid, uuid]).take(1).subscribe(arr => testObjs = arr); expect(testObjs[0].uuid).toBe(uuid); expect(testObjs[0].foo).toBe("bar"); - expect(testObjs[0].test()).toBe("bar" + uuid); expect(testObjs[1].uuid).toBe(uuid); expect(testObjs[1].foo).toBe("bar"); - expect(testObjs[1].test()).toBe("bar" + uuid); }); }); diff --git a/src/app/core/cache/object-cache.service.ts b/src/app/core/cache/object-cache.service.ts index b801421ce3..ec0bea4a97 100644 --- a/src/app/core/cache/object-cache.service.ts +++ b/src/app/core/cache/object-cache.service.ts @@ -4,6 +4,7 @@ import { ObjectCacheState, ObjectCacheEntry, CacheableObject } from "./object-ca import { AddToObjectCacheAction, RemoveFromObjectCacheAction } from "./object-cache.actions"; import { Observable } from "rxjs"; import { hasNoValue } from "../../shared/empty.util"; +import { GenericConstructor } from "../shared/generic-constructor"; /** * A service to interact with the object cache @@ -39,34 +40,53 @@ export class ObjectCacheService { /** * Get an observable of the object with the specified UUID * + * The type needs to be specified as well, in order to turn + * the cached plain javascript object in to an instance of + * a class. + * + * e.g. get('c96588c6-72d3-425d-9d47-fa896255a695', Item) + * * @param uuid * The UUID of the object to get + * @param type + * The type of the object to get * @return Observable * An observable of the requested object */ - get(uuid: string): Observable { + get(uuid: string, type: GenericConstructor): Observable { return this.store.select('core', 'cache', 'object', uuid) .filter(entry => this.isValid(entry)) .distinctUntilChanged() - .map((entry: ObjectCacheEntry) => entry.data); + .map((entry: ObjectCacheEntry) => Object.assign(new type(), entry.data)); } - getBySelfLink(href: string): Observable { + getBySelfLink(href: string, type: GenericConstructor): Observable { return this.store.select('core', 'index', 'href', href) - .flatMap((uuid: string) => this.get(uuid)) + .flatMap((uuid: string) => this.get(uuid, type)) } /** - * Get an observable for an array of objects + * Get an observable for an array of objects of the same type * with the specified UUIDs * + * The type needs to be specified as well, in order to turn + * the cached plain javascript object in to an instance of + * a class. + * + * e.g. getList([ + * 'c96588c6-72d3-425d-9d47-fa896255a695', + * 'cff860da-cf5f-4fda-b8c9-afb7ec0b2d9e' + * ], Collection) + * * @param uuids * An array of UUIDs of the objects to get + * @param type + * The type of the objects to get * @return Observable> */ - getList(uuids: Array): Observable> { + getList(uuids: Array, type: GenericConstructor): Observable> { return Observable.combineLatest( - uuids.map((id: string) => this.get(id)) + uuids.map((id: string) => this.get(id, type)) ); } diff --git a/src/app/core/shared/bundle.model.ts b/src/app/core/shared/bundle.model.ts index ec13a78711..7c2f6b05d4 100644 --- a/src/app/core/shared/bundle.model.ts +++ b/src/app/core/shared/bundle.model.ts @@ -1,10 +1,8 @@ -import { inheritSerialization } from "cerialize"; import { DSpaceObject } from "./dspace-object.model"; import { Bitstream } from "./bitstream.model"; import { Item } from "./item.model"; import { RemoteData } from "../data/remote-data"; -@inheritSerialization(DSpaceObject) export class Bundle extends DSpaceObject { /** * The primary bitstream of this Bundle diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index 5d43c1afab..008acd4fe6 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -1,4 +1,3 @@ -import { inheritSerialization, autoserialize, autoserializeAs } from "cerialize"; import { DSpaceObject } from "./dspace-object.model"; import { Collection } from "./collection.model"; import { RemoteData } from "../data/remote-data"; @@ -6,39 +5,37 @@ import { Bundle } from "./bundle.model"; import { Bitstream } from "./bitstream.model"; import { Observable } from "rxjs"; -@inheritSerialization(DSpaceObject) export class Item extends DSpaceObject { - /** - * A string representing the unique handle of this Item - */ - @autoserialize - handle: string; + /** + * A string representing the unique handle of this Item + */ + handle: string; - /** - * The Date of the last modification of this Item - */ - lastModified: Date; + /** + * The Date of the last modification of this Item + */ + lastModified: Date; - /** - * A boolean representing if this Item is currently archived or not - */ - isArchived: boolean; + /** + * A boolean representing if this Item is currently archived or not + */ + isArchived: boolean; - /** - * A boolean representing if this Item is currently withdrawn or not - */ - isWithdrawn: boolean; + /** + * A boolean representing if this Item is currently withdrawn or not + */ + isWithdrawn: boolean; /** * An array of Collections that are direct parents of this Item */ parents: Array>; - /** - * The Collection that owns this Item - */ - owner: Collection; + /** + * The Collection that owns this Item + */ + owner: Collection; bundles: Array>; diff --git a/src/typings.d.ts b/src/typings.d.ts index 2e0285e9b4..be2597c600 100644 --- a/src/typings.d.ts +++ b/src/typings.d.ts @@ -78,3 +78,5 @@ declare module "*.json" { const value: any; export default value; } + +declare module "reflect-metadata"; diff --git a/yarn.lock b/yarn.lock index eac42106d8..f4c40f1e12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4717,9 +4717,9 @@ reflect-metadata@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.2.tgz#ea23e5823dc830f292822bd3da9b89fd57bffb03" -reflect-metadata@0.1.8, reflect-metadata@^0.1.2: - version "0.1.8" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.8.tgz#72426d570b60776e3688968bd5ab9537a15cecf6" +reflect-metadata@^0.1.10, reflect-metadata@^0.1.2: + version "0.1.10" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.10.tgz#b4f83704416acad89988c9b15635d47e03b9344a" regenerate@^1.2.1: version "1.3.2"