mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-10 03:23:07 +00:00
fixed issue where resources that were fetched from a list where not able to be rendered separately
This commit is contained in:
@@ -30,37 +30,33 @@ export class RemoteDataBuildService {
|
||||
href: string,
|
||||
normalizedType: GenericConstructor<TNormalized>
|
||||
): RemoteData<TDomain> {
|
||||
const requestObs = this.store.select<RequestEntry>('core', 'data', 'request', href);
|
||||
const responseCacheObs = this.responseCache.get(href);
|
||||
const requestHrefObs = this.objectCache.getRequestHrefBySelfLink(href);
|
||||
|
||||
const requestPending = requestObs.map((entry: RequestEntry) => hasValue(entry) && entry.requestPending).distinctUntilChanged();
|
||||
const requestObs = Observable.race(
|
||||
this.store.select<RequestEntry>('core', 'data', 'request', href).filter(entry => hasValue(entry)),
|
||||
requestHrefObs.flatMap(requestHref =>
|
||||
this.store.select<RequestEntry>('core', 'data', 'request', requestHref)).filter(entry => hasValue(entry))
|
||||
);
|
||||
|
||||
const responsePending = requestObs.map((entry: RequestEntry) => hasValue(entry) && entry.responsePending).distinctUntilChanged();
|
||||
const responseCacheObs = Observable.race(
|
||||
this.responseCache.get(href).filter(entry => hasValue(entry)),
|
||||
requestHrefObs.flatMap(requestHref => this.responseCache.get(requestHref)).filter(entry => hasValue(entry))
|
||||
);
|
||||
|
||||
const requestPending = requestObs.map((entry: RequestEntry) => entry.requestPending).distinctUntilChanged();
|
||||
|
||||
const responsePending = requestObs.map((entry: RequestEntry) => entry.responsePending).distinctUntilChanged();
|
||||
|
||||
const isSuccessFul = responseCacheObs
|
||||
.map((entry: ResponseCacheEntry) => hasValue(entry) && entry.response.isSuccessful).distinctUntilChanged();
|
||||
.map((entry: ResponseCacheEntry) => entry.response.isSuccessful).distinctUntilChanged();
|
||||
|
||||
const errorMessage = responseCacheObs
|
||||
.filter((entry: ResponseCacheEntry) => hasValue(entry) && !entry.response.isSuccessful)
|
||||
.filter((entry: ResponseCacheEntry) => !entry.response.isSuccessful)
|
||||
.map((entry: ResponseCacheEntry) => (<ErrorResponse> entry.response).errorMessage)
|
||||
.distinctUntilChanged();
|
||||
|
||||
const payload =
|
||||
Observable.race(
|
||||
this.objectCache.getBySelfLink<TNormalized>(href, normalizedType),
|
||||
responseCacheObs
|
||||
.filter((entry: ResponseCacheEntry) => hasValue(entry) && 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) => {
|
||||
const payload = this.objectCache.getBySelfLink<TNormalized>(href, normalizedType)
|
||||
.map((normalized: TNormalized) => {
|
||||
return this.build<TNormalized, TDomain>(normalized);
|
||||
});
|
||||
|
||||
@@ -78,23 +74,24 @@ export class RemoteDataBuildService {
|
||||
href: string,
|
||||
normalizedType: GenericConstructor<TNormalized>
|
||||
): RemoteData<TDomain[]> {
|
||||
const requestObs = this.store.select<RequestEntry>('core', 'data', 'request', href);
|
||||
const responseCacheObs = this.responseCache.get(href);
|
||||
const requestObs = this.store.select<RequestEntry>('core', 'data', 'request', href)
|
||||
.filter(entry => hasValue(entry));
|
||||
const responseCacheObs = this.responseCache.get(href).filter(entry => hasValue(entry));
|
||||
|
||||
const requestPending = requestObs.map((entry: RequestEntry) => hasValue(entry) && entry.requestPending).distinctUntilChanged();
|
||||
const requestPending = requestObs.map((entry: RequestEntry) => entry.requestPending).distinctUntilChanged();
|
||||
|
||||
const responsePending = requestObs.map((entry: RequestEntry) => hasValue(entry) && entry.responsePending).distinctUntilChanged();
|
||||
const responsePending = requestObs.map((entry: RequestEntry) => entry.responsePending).distinctUntilChanged();
|
||||
|
||||
const isSuccessFul = responseCacheObs
|
||||
.map((entry: ResponseCacheEntry) => hasValue(entry) && entry.response.isSuccessful).distinctUntilChanged();
|
||||
.map((entry: ResponseCacheEntry) => entry.response.isSuccessful).distinctUntilChanged();
|
||||
|
||||
const errorMessage = responseCacheObs
|
||||
.filter((entry: ResponseCacheEntry) => hasValue(entry) && !entry.response.isSuccessful)
|
||||
.filter((entry: ResponseCacheEntry) => !entry.response.isSuccessful)
|
||||
.map((entry: ResponseCacheEntry) => (<ErrorResponse> entry.response).errorMessage)
|
||||
.distinctUntilChanged();
|
||||
|
||||
const payload = responseCacheObs
|
||||
.filter((entry: ResponseCacheEntry) => hasValue(entry) && entry.response.isSuccessful)
|
||||
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
|
||||
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).resourceUUIDs)
|
||||
.flatMap((resourceUUIDs: Array<string>) => {
|
||||
return this.objectCache.getList(resourceUUIDs, normalizedType)
|
||||
|
9
src/app/core/cache/object-cache.actions.ts
vendored
9
src/app/core/cache/object-cache.actions.ts
vendored
@@ -20,6 +20,7 @@ export class AddToObjectCacheAction implements Action {
|
||||
objectToCache: CacheableObject;
|
||||
timeAdded: number;
|
||||
msToLive: number;
|
||||
requestHref: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -31,9 +32,13 @@ export class AddToObjectCacheAction implements Action {
|
||||
* the time it was added
|
||||
* @param msToLive
|
||||
* the amount of milliseconds before it should expire
|
||||
* @param requestHref
|
||||
* The href of the request that resulted in this object
|
||||
* This isn't necessarily the same as the object's self
|
||||
* link, it could have been part of a list for example
|
||||
*/
|
||||
constructor(objectToCache: CacheableObject, timeAdded: number, msToLive: number) {
|
||||
this.payload = { objectToCache, timeAdded, msToLive };
|
||||
constructor(objectToCache: CacheableObject, timeAdded: number, msToLive: number, requestHref: string) {
|
||||
this.payload = { objectToCache, timeAdded, msToLive, requestHref };
|
||||
}
|
||||
}
|
||||
|
||||
|
15
src/app/core/cache/object-cache.reducer.spec.ts
vendored
15
src/app/core/cache/object-cache.reducer.spec.ts
vendored
@@ -24,7 +24,8 @@ describe("objectCacheReducer", () => {
|
||||
foo: "bar"
|
||||
},
|
||||
timeAdded: new Date().getTime(),
|
||||
msToLive: 900000
|
||||
msToLive: 900000,
|
||||
requestHref: "https://rest.api/endpoint/uuid1"
|
||||
},
|
||||
[uuid2]: {
|
||||
data: {
|
||||
@@ -32,7 +33,8 @@ describe("objectCacheReducer", () => {
|
||||
foo: "baz"
|
||||
},
|
||||
timeAdded: new Date().getTime(),
|
||||
msToLive: 900000
|
||||
msToLive: 900000,
|
||||
requestHref: "https://rest.api/endpoint/uuid2"
|
||||
}
|
||||
};
|
||||
deepFreeze(testState);
|
||||
@@ -56,7 +58,8 @@ describe("objectCacheReducer", () => {
|
||||
const objectToCache = {uuid: uuid1};
|
||||
const timeAdded = new Date().getTime();
|
||||
const msToLive = 900000;
|
||||
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive);
|
||||
const requestHref = "https://rest.api/endpoint/uuid1";
|
||||
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive, requestHref);
|
||||
const newState = objectCacheReducer(state, action);
|
||||
|
||||
expect(newState[uuid1].data).toEqual(objectToCache);
|
||||
@@ -68,7 +71,8 @@ describe("objectCacheReducer", () => {
|
||||
const objectToCache = {uuid: uuid1, foo: "baz", somethingElse: true};
|
||||
const timeAdded = new Date().getTime();
|
||||
const msToLive = 900000;
|
||||
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive);
|
||||
const requestHref = "https://rest.api/endpoint/uuid1";
|
||||
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive, requestHref);
|
||||
const newState = objectCacheReducer(testState, action);
|
||||
|
||||
expect(newState[uuid1].data['foo']).toBe("baz");
|
||||
@@ -80,7 +84,8 @@ describe("objectCacheReducer", () => {
|
||||
const objectToCache = {uuid: uuid1};
|
||||
const timeAdded = new Date().getTime();
|
||||
const msToLive = 900000;
|
||||
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive);
|
||||
const requestHref = "https://rest.api/endpoint/uuid1";
|
||||
const action = new AddToObjectCacheAction(objectToCache, timeAdded, msToLive, requestHref);
|
||||
deepFreeze(state);
|
||||
|
||||
objectCacheReducer(state, action);
|
||||
|
4
src/app/core/cache/object-cache.reducer.ts
vendored
4
src/app/core/cache/object-cache.reducer.ts
vendored
@@ -22,6 +22,7 @@ export class ObjectCacheEntry implements CacheEntry {
|
||||
data: CacheableObject;
|
||||
timeAdded: number;
|
||||
msToLive: number;
|
||||
requestHref: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +84,8 @@ function addToObjectCache(state: ObjectCacheState, action: AddToObjectCacheActio
|
||||
[action.payload.objectToCache.uuid]: {
|
||||
data: action.payload.objectToCache,
|
||||
timeAdded: action.payload.timeAdded,
|
||||
msToLive: action.payload.msToLive
|
||||
msToLive: action.payload.msToLive,
|
||||
requestHref: action.payload.requestHref
|
||||
}
|
||||
});
|
||||
}
|
||||
|
29
src/app/core/cache/object-cache.service.ts
vendored
29
src/app/core/cache/object-cache.service.ts
vendored
@@ -22,9 +22,13 @@ export class ObjectCacheService {
|
||||
* The object to add
|
||||
* @param msToLive
|
||||
* The number of milliseconds it should be cached for
|
||||
* @param requestHref
|
||||
* The href of the request that resulted in this object
|
||||
* This isn't necessarily the same as the object's self
|
||||
* link, it could have been part of a list for example
|
||||
*/
|
||||
add(objectToCache: CacheableObject, msToLive: number): void {
|
||||
this.store.dispatch(new AddToObjectCacheAction(objectToCache, new Date().getTime(), msToLive));
|
||||
add(objectToCache: CacheableObject, msToLive: number, requestHref: string): void {
|
||||
this.store.dispatch(new AddToObjectCacheAction(objectToCache, new Date().getTime(), msToLive, requestHref));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,9 +58,7 @@ export class ObjectCacheService {
|
||||
* An observable of the requested object
|
||||
*/
|
||||
get<T extends CacheableObject>(uuid: string, type: GenericConstructor<T>): Observable<T> {
|
||||
return this.store.select<ObjectCacheEntry>('core', 'cache', 'object', uuid)
|
||||
.filter(entry => this.isValid(entry))
|
||||
.distinctUntilChanged()
|
||||
return this.getEntry(uuid)
|
||||
.map((entry: ObjectCacheEntry) => <T> Object.assign(new type(), entry.data));
|
||||
}
|
||||
|
||||
@@ -65,6 +67,23 @@ export class ObjectCacheService {
|
||||
.flatMap((uuid: string) => this.get(uuid, type))
|
||||
}
|
||||
|
||||
private getEntry(uuid: string): Observable<ObjectCacheEntry> {
|
||||
return this.store.select<ObjectCacheEntry>('core', 'cache', 'object', uuid)
|
||||
.filter(entry => this.isValid(entry))
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
|
||||
getRequestHref(uuid: string): Observable<string> {
|
||||
return this.getEntry(uuid)
|
||||
.map((entry: ObjectCacheEntry) => entry.requestHref)
|
||||
.distinctUntilChanged();
|
||||
}
|
||||
|
||||
getRequestHrefBySelfLink(self: string): Observable<string> {
|
||||
return this.store.select<string>('core', 'index', 'href', self)
|
||||
.flatMap((uuid: string) => this.getRequestHref(uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an observable for an array of objects of the same type
|
||||
* with the specified UUIDs
|
||||
|
@@ -45,7 +45,7 @@ export class RequestEffects {
|
||||
})
|
||||
.flatMap((entry: RequestEntry) => {
|
||||
return this.restApi.get(entry.request.href)
|
||||
.map((data: DSpaceRESTV2Response) => this.processEmbedded(data._embedded))
|
||||
.map((data: DSpaceRESTV2Response) => this.processEmbedded(data._embedded, entry.request.href))
|
||||
.map((ids: Array<string>) => new SuccessResponse(ids))
|
||||
.do((response: Response) => this.responseCache.add(entry.request.href, response, this.EnvConfig.cache.msToLive))
|
||||
.map((response: Response) => new RequestCompleteAction(entry.request.href))
|
||||
@@ -54,25 +54,25 @@ export class RequestEffects {
|
||||
.map((response: Response) => new RequestCompleteAction(entry.request.href)));
|
||||
});
|
||||
|
||||
protected processEmbedded(_embedded: any): Array<string> {
|
||||
protected processEmbedded(_embedded: any, requestHref): Array<string> {
|
||||
|
||||
if (isNotEmpty(_embedded)) {
|
||||
if (isObjectLevel(_embedded)) {
|
||||
return this.deserializeAndCache(_embedded);
|
||||
return this.deserializeAndCache(_embedded, requestHref);
|
||||
}
|
||||
else {
|
||||
let uuids = [];
|
||||
Object.keys(_embedded)
|
||||
.filter(property => _embedded.hasOwnProperty(property))
|
||||
.forEach(property => {
|
||||
uuids = [...uuids, ...this.deserializeAndCache(_embedded[property])];
|
||||
uuids = [...uuids, ...this.deserializeAndCache(_embedded[property], requestHref)];
|
||||
});
|
||||
return uuids;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected deserializeAndCache(obj): Array<string> {
|
||||
protected deserializeAndCache(obj, requestHref): Array<string> {
|
||||
let type: ResourceType;
|
||||
const isArray = Array.isArray(obj);
|
||||
|
||||
@@ -96,19 +96,19 @@ export class RequestEffects {
|
||||
if (isArray) {
|
||||
obj.forEach(o => {
|
||||
if (isNotEmpty(o._embedded)) {
|
||||
this.processEmbedded(o._embedded);
|
||||
this.processEmbedded(o._embedded, requestHref);
|
||||
}
|
||||
});
|
||||
const normalizedObjArr = serializer.deserializeArray(obj);
|
||||
normalizedObjArr.forEach(t => this.addToObjectCache(t));
|
||||
normalizedObjArr.forEach(t => this.addToObjectCache(t, requestHref));
|
||||
return normalizedObjArr.map(t => t.uuid);
|
||||
}
|
||||
else {
|
||||
if (isNotEmpty(obj._embedded)) {
|
||||
this.processEmbedded(obj._embedded);
|
||||
this.processEmbedded(obj._embedded, requestHref);
|
||||
}
|
||||
const normalizedObj = serializer.deserialize(obj);
|
||||
this.addToObjectCache(normalizedObj);
|
||||
this.addToObjectCache(normalizedObj, requestHref);
|
||||
return [normalizedObj.uuid];
|
||||
}
|
||||
|
||||
@@ -125,10 +125,10 @@ export class RequestEffects {
|
||||
}
|
||||
}
|
||||
|
||||
protected addToObjectCache(co: CacheableObject): void {
|
||||
protected addToObjectCache(co: CacheableObject, requestHref: string): void {
|
||||
if (hasNoValue(co) || hasNoValue(co.uuid)) {
|
||||
throw new Error('The server returned an invalid object');
|
||||
}
|
||||
this.objectCache.add(co, this.EnvConfig.cache.msToLive);
|
||||
this.objectCache.add(co, this.EnvConfig.cache.msToLive, requestHref);
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,9 @@ import { RequestConfigureAction, RequestExecuteAction } from "./request.actions"
|
||||
import { ResponseCacheService } from "../cache/response-cache.service";
|
||||
import { ObjectCacheService } from "../cache/object-cache.service";
|
||||
import { CacheableObject } from "../cache/object-cache.reducer";
|
||||
import { ResponseCacheEntry } from "../cache/response-cache.reducer";
|
||||
import { request } from "http";
|
||||
import { SuccessResponse } from "../cache/response-cache.models";
|
||||
|
||||
@Injectable()
|
||||
export class RequestService {
|
||||
@@ -35,7 +38,19 @@ export class RequestService {
|
||||
}
|
||||
|
||||
configure<T extends CacheableObject>(request: Request<T>): void {
|
||||
const isCached = this.objectCache.hasBySelfLink(request.href);
|
||||
let isCached = this.objectCache.hasBySelfLink(request.href);
|
||||
|
||||
if (!isCached && this.responseCache.has(request.href)) {
|
||||
//if it isn't cached it may be a list endpoint, if so verify
|
||||
//every object included in the response is still cached
|
||||
this.responseCache.get(request.href)
|
||||
.take(1)
|
||||
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
|
||||
.map((entry: ResponseCacheEntry) => (<SuccessResponse> entry.response).resourceUUIDs)
|
||||
.map((resourceUUIDs: Array<string>) => resourceUUIDs.every(uuid => this.objectCache.has(uuid)))
|
||||
.subscribe(c => isCached = c);
|
||||
}
|
||||
|
||||
const isPending = this.isPending(request.href);
|
||||
|
||||
if (!(isCached || isPending)) {
|
||||
|
@@ -35,7 +35,7 @@ export const COMMUNITIES = {
|
||||
],
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "http://dspace7.4science.it/dspace-spring-rest/api/core/community/9076bd16-e69a-48d6-9e41-0238cb40d863"
|
||||
"href": "/communities/6631"
|
||||
},
|
||||
"collections": [
|
||||
{ "href": "/collections/5179" }
|
||||
@@ -78,7 +78,7 @@ export const COMMUNITIES = {
|
||||
],
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "http://dspace7.4science.it/dspace-spring-rest/api/core/community/9076bd16-e69a-48d6-9e41-0238cb40d863"
|
||||
"href": "/communities/2365"
|
||||
},
|
||||
"collections": [
|
||||
{ "href": "/collections/6547" }
|
||||
|
Reference in New Issue
Block a user