mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-10 03:23:07 +00:00
fixed issues with related resources that have a single link for a hasMany relationship (e.g. item.bitstreams)
This commit is contained in:
@@ -16,7 +16,7 @@ export const getMapsTo = function(target: any) {
|
||||
return Reflect.getOwnMetadata(mapsToMetadataKey, target);
|
||||
};
|
||||
|
||||
export const relationship = function(value: ResourceType): any {
|
||||
export const relationship = function(value: ResourceType, isList: boolean = false): any {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
if (!target || !propertyKey) {
|
||||
return;
|
||||
@@ -28,11 +28,11 @@ export const relationship = function(value: ResourceType): any {
|
||||
}
|
||||
relationshipMap.set(target.constructor, metaDataList);
|
||||
|
||||
return Reflect.metadata(relationshipKey, value).apply(this, arguments);
|
||||
return Reflect.metadata(relationshipKey, { resourceType: value, isList }).apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
export const getResourceType = function(target: any, propertyKey: string) {
|
||||
export const getRelationMetadata = function(target: any, propertyKey: string) {
|
||||
return Reflect.getMetadata(relationshipKey, target, propertyKey);
|
||||
};
|
||||
|
||||
|
@@ -12,7 +12,7 @@ import { ErrorResponse, SuccessResponse } from "../response-cache.models";
|
||||
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 { getMapsTo, getRelationMetadata, getRelationships } from "./build-decorators";
|
||||
import { NormalizedObjectFactory } from "../models/normalized-object-factory";
|
||||
import { Request } from "../../data/request.models";
|
||||
|
||||
@@ -64,11 +64,26 @@ export class RemoteDataBuildService {
|
||||
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).pageInfo)
|
||||
.distinctUntilChanged();
|
||||
|
||||
const payload = this.objectCache.getBySelfLink<TNormalized>(href, normalizedType)
|
||||
.map((normalized: TNormalized) => {
|
||||
const payload =
|
||||
Observable.race(
|
||||
this.objectCache.getBySelfLink<TNormalized>(href, normalizedType),
|
||||
responseCacheObs
|
||||
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
|
||||
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).resourceUUIDs)
|
||||
.flatMap((resourceUUIDs: Array<string>) => {
|
||||
if (isNotEmpty(resourceUUIDs)) {
|
||||
return this.objectCache.get(resourceUUIDs[0], normalizedType);
|
||||
}
|
||||
else {
|
||||
return Observable.of(undefined);
|
||||
}
|
||||
})
|
||||
.distinctUntilChanged()
|
||||
).map((normalized: TNormalized) => {
|
||||
return this.build<TNormalized, TDomain>(normalized);
|
||||
});
|
||||
|
||||
|
||||
return new RemoteData(
|
||||
href,
|
||||
requestPending,
|
||||
@@ -143,7 +158,7 @@ export class RemoteDataBuildService {
|
||||
|
||||
relationships.forEach((relationship: string) => {
|
||||
if (hasValue(normalized[relationship])) {
|
||||
const resourceType = getResourceType(normalized, relationship);
|
||||
const { resourceType, isList } = getRelationMetadata(normalized, relationship);
|
||||
const resourceConstructor = NormalizedObjectFactory.getConstructor(resourceType);
|
||||
if (Array.isArray(normalized[relationship])) {
|
||||
// without the setTimeout, the actions inside requestService.configure
|
||||
@@ -168,7 +183,14 @@ export class RemoteDataBuildService {
|
||||
this.requestService.configure(new Request(normalized[relationship]));
|
||||
},0);
|
||||
|
||||
links[relationship] = this.buildSingle(normalized[relationship], resourceConstructor);
|
||||
// The rest API can return a single URL to represent a list of resources (e.g. /items/:id/bitstreams)
|
||||
// in that case only 1 href will be stored in the normalized obj (so the isArray above fails),
|
||||
// but it should still be built as a list
|
||||
if (isList) {
|
||||
links[relationship] = this.buildList(normalized[relationship], resourceConstructor);
|
||||
} else {
|
||||
links[relationship] = this.buildSingle(normalized[relationship], resourceConstructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
11
src/app/core/cache/models/normalized-bitstream-format.model.ts
vendored
Normal file
11
src/app/core/cache/models/normalized-bitstream-format.model.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { NormalizedObject } from "./normalized-object.model";
|
||||
import { inheritSerialization } from "cerialize";
|
||||
|
||||
@inheritSerialization(NormalizedObject)
|
||||
export class NormalizedBitstreamFormat extends NormalizedObject {
|
||||
//TODO this class was created as a placeholder when we connected to the live rest api
|
||||
|
||||
get uuid(): string {
|
||||
return this.self;
|
||||
}
|
||||
}
|
@@ -9,10 +9,10 @@ import { ResourceType } from "../../shared/resource-type";
|
||||
export class NormalizedBitstream extends NormalizedDSpaceObject {
|
||||
|
||||
/**
|
||||
* The size of this bitstream in bytes(?)
|
||||
* The size of this bitstream in bytes
|
||||
*/
|
||||
@autoserialize
|
||||
size: number;
|
||||
sizeBytes: number;
|
||||
|
||||
/**
|
||||
* The relative path to this Bitstream's file
|
||||
@@ -42,14 +42,14 @@ export class NormalizedBitstream extends NormalizedDSpaceObject {
|
||||
* An array of Bundles that are direct parents of this Bitstream
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Item)
|
||||
@relationship(ResourceType.Item, true)
|
||||
parents: Array<string>;
|
||||
|
||||
/**
|
||||
* The Bundle that owns this Bitstream
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Item)
|
||||
@relationship(ResourceType.Item, false)
|
||||
owner: string;
|
||||
|
||||
/**
|
||||
|
@@ -11,7 +11,7 @@ export class NormalizedBundle extends NormalizedDSpaceObject {
|
||||
* The primary bitstream of this Bundle
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Bitstream)
|
||||
@relationship(ResourceType.Bitstream, false)
|
||||
primaryBitstream: string;
|
||||
|
||||
/**
|
||||
@@ -25,6 +25,6 @@ export class NormalizedBundle extends NormalizedDSpaceObject {
|
||||
owner: string;
|
||||
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Bitstream)
|
||||
@relationship(ResourceType.Bitstream, true)
|
||||
bitstreams: Array<string>;
|
||||
}
|
||||
|
@@ -18,25 +18,25 @@ export class NormalizedCollection extends NormalizedDSpaceObject {
|
||||
* The Bitstream that represents the logo of this Collection
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Bitstream)
|
||||
@relationship(ResourceType.Bitstream, false)
|
||||
logo: string;
|
||||
|
||||
/**
|
||||
* An array of Communities that are direct parents of this Collection
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Community)
|
||||
@relationship(ResourceType.Community, true)
|
||||
parents: Array<string>;
|
||||
|
||||
/**
|
||||
* The Community that owns this Collection
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Community)
|
||||
@relationship(ResourceType.Community, false)
|
||||
owner: string;
|
||||
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Item)
|
||||
@relationship(ResourceType.Item, true)
|
||||
items: Array<string>;
|
||||
|
||||
}
|
||||
|
@@ -18,25 +18,25 @@ export class NormalizedCommunity extends NormalizedDSpaceObject {
|
||||
* The Bitstream that represents the logo of this Community
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Bitstream)
|
||||
@relationship(ResourceType.Bitstream, false)
|
||||
logo: string;
|
||||
|
||||
/**
|
||||
* An array of Communities that are direct parents of this Community
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Community)
|
||||
@relationship(ResourceType.Community, true)
|
||||
parents: Array<string>;
|
||||
|
||||
/**
|
||||
* The Community that owns this Community
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Community)
|
||||
@relationship(ResourceType.Community, false)
|
||||
owner: string;
|
||||
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Collection)
|
||||
@relationship(ResourceType.Collection, true)
|
||||
collections: Array<string>;
|
||||
|
||||
}
|
||||
|
@@ -1,13 +1,19 @@
|
||||
import { autoserialize, autoserializeAs } from "cerialize";
|
||||
import { CacheableObject } from "../object-cache.reducer";
|
||||
import { autoserialize, autoserializeAs, inheritSerialization } from "cerialize";
|
||||
import { Metadatum } from "../../shared/metadatum.model";
|
||||
import { ResourceType } from "../../shared/resource-type";
|
||||
import { NormalizedObject } from "./normalized-object.model";
|
||||
|
||||
/**
|
||||
* An abstract model class for a DSpaceObject.
|
||||
*/
|
||||
export abstract class NormalizedDSpaceObject implements CacheableObject {
|
||||
export abstract class NormalizedDSpaceObject extends NormalizedObject{
|
||||
|
||||
/**
|
||||
* The link to the rest endpoint where this object can be found
|
||||
*
|
||||
* Repeated here to make the serialization work,
|
||||
* inheritSerialization doesn't seem to work for more than one level
|
||||
*/
|
||||
@autoserialize
|
||||
self: string;
|
||||
|
||||
@@ -22,6 +28,9 @@ export abstract class NormalizedDSpaceObject implements CacheableObject {
|
||||
|
||||
/**
|
||||
* The universally unique identifier of this DSpaceObject
|
||||
*
|
||||
* Repeated here to make the serialization work,
|
||||
* inheritSerialization doesn't seem to work for more than one level
|
||||
*/
|
||||
@autoserialize
|
||||
uuid: string;
|
||||
|
@@ -42,17 +42,17 @@ export class NormalizedItem extends NormalizedDSpaceObject {
|
||||
* An array of Collections that are direct parents of this Item
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Collection)
|
||||
@relationship(ResourceType.Collection, true)
|
||||
parents: Array<string>;
|
||||
|
||||
/**
|
||||
* The Collection that owns this Item
|
||||
*/
|
||||
@autoserializeAs(String, 'owningCollection')
|
||||
@relationship(ResourceType.Collection)
|
||||
@relationship(ResourceType.Collection, false)
|
||||
owner: string;
|
||||
|
||||
@autoserialize
|
||||
@relationship(ResourceType.Bitstream)
|
||||
@relationship(ResourceType.Bitstream, true)
|
||||
bitstreams: Array<string>;
|
||||
}
|
||||
|
@@ -6,13 +6,20 @@ import { NormalizedCollection } from "./normalized-collection.model";
|
||||
import { GenericConstructor } from "../../shared/generic-constructor";
|
||||
import { NormalizedCommunity } from "./normalized-community.model";
|
||||
import { ResourceType } from "../../shared/resource-type";
|
||||
import { NormalizedObject } from "./normalized-object.model";
|
||||
import { NormalizedBitstreamFormat } from "./normalized-bitstream-format.model";
|
||||
|
||||
export class NormalizedObjectFactory {
|
||||
public static getConstructor(type: ResourceType): GenericConstructor<NormalizedDSpaceObject> {
|
||||
public static getConstructor(type: ResourceType): GenericConstructor<NormalizedObject> {
|
||||
switch (type) {
|
||||
case ResourceType.Bitstream: {
|
||||
return NormalizedBitstream
|
||||
}
|
||||
// commented out for now, bitstreamformats aren't used in the UI yet
|
||||
// and slow things down noticeably
|
||||
// case ResourceType.BitstreamFormat: {
|
||||
// return NormalizedBitstreamFormat
|
||||
// }
|
||||
case ResourceType.Bundle: {
|
||||
return NormalizedBundle
|
||||
}
|
||||
|
20
src/app/core/cache/models/normalized-object.model.ts
vendored
Normal file
20
src/app/core/cache/models/normalized-object.model.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import { CacheableObject } from "../object-cache.reducer";
|
||||
import { autoserialize } from "cerialize";
|
||||
/**
|
||||
* An abstract model class for a NormalizedObject.
|
||||
*/
|
||||
export abstract class NormalizedObject implements CacheableObject {
|
||||
|
||||
/**
|
||||
* The link to the rest endpoint where this object can be found
|
||||
*/
|
||||
@autoserialize
|
||||
self: string;
|
||||
|
||||
/**
|
||||
* The universally unique identifier of this Object
|
||||
*/
|
||||
@autoserialize
|
||||
uuid: string;
|
||||
|
||||
}
|
@@ -72,7 +72,16 @@ export class RequestEffects {
|
||||
.filter(property => data.hasOwnProperty(property))
|
||||
.filter(property => hasValue(data[property]))
|
||||
.forEach(property => {
|
||||
uuids = [...uuids, ...this.deserializeAndCache(data[property], requestHref)];
|
||||
let propertyUUIDs;
|
||||
|
||||
if (isPaginatedResponse(data[property])) {
|
||||
propertyUUIDs = this.process(data[property], requestHref);
|
||||
}
|
||||
else {
|
||||
propertyUUIDs = this.deserializeAndCache(data[property], requestHref);
|
||||
}
|
||||
|
||||
uuids = [...uuids, ...propertyUUIDs];
|
||||
});
|
||||
return uuids;
|
||||
}
|
||||
@@ -122,13 +131,15 @@ export class RequestEffects {
|
||||
}
|
||||
else {
|
||||
//TODO move check to Validator?
|
||||
throw new Error(`The server returned an object with an unknown a known type: ${type}`);
|
||||
// throw new Error(`The server returned an object with an unknown a known type: ${type}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
//TODO move check to Validator
|
||||
throw new Error(`The server returned an object without a type: ${JSON.stringify(obj)}`);
|
||||
// throw new Error(`The server returned an object without a type: ${JSON.stringify(obj)}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,9 +5,9 @@ import { Item } from "./item.model";
|
||||
export class Bitstream extends DSpaceObject {
|
||||
|
||||
/**
|
||||
* The size of this bitstream in bytes(?)
|
||||
* The size of this bitstream in bytes
|
||||
*/
|
||||
size: number;
|
||||
sizeBytes: number;
|
||||
|
||||
/**
|
||||
* The mime type of this Bitstream
|
||||
|
@@ -5,6 +5,7 @@
|
||||
export enum ResourceType {
|
||||
Bundle = <any> "bundle",
|
||||
Bitstream = <any> "bitstream",
|
||||
BitstreamFormat = <any> "bitstreamformat",
|
||||
Item = <any> "item",
|
||||
Collection = <any> "collection",
|
||||
Community = <any> "community"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<ds-metadata-field-wrapper [label]="label | translate">
|
||||
<div class="collections">
|
||||
<a *ngFor="let collection of (collections | async); let last=last;" [href]="collection?.self">
|
||||
<a *ngFor="let collection of (collections | async); let last=last;" [routerLink]="['/collections', collection.id]">
|
||||
<span>{{collection?.name}}</span><span *ngIf="!last" [innerHTML]="separator"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
@@ -19,7 +19,7 @@ export class CollectionsComponent implements OnInit {
|
||||
|
||||
label : string = "item.page.collections";
|
||||
|
||||
separator: string = "<br/>"
|
||||
separator: string = "<br/>";
|
||||
|
||||
collections: Observable<Collection[]>;
|
||||
|
||||
@@ -38,7 +38,7 @@ export class CollectionsComponent implements OnInit {
|
||||
//TODO this should use parents, but the collections
|
||||
// for an Item aren't returned by the REST API yet,
|
||||
// only the owning collection
|
||||
this.collections = this.rdbs.aggregate([this.item.owner]).payload
|
||||
this.collections = this.item.owner.payload.map(c => [c]);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="file-section">
|
||||
<a *ngFor="let file of (files | async); let last=last;" [href]="file?.url">
|
||||
<span>{{file?.name}}</span>
|
||||
<span>({{(file?.size) | dsFileSize }})</span>
|
||||
<span>({{(file?.sizeBytes) | dsFileSize }})</span>
|
||||
<span *ngIf="!last" innerHTML="{{separator}}"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user