Refactored Data Services

This commit is contained in:
Art Lowel
2017-02-22 12:03:22 +01:00
parent 9364c32ae2
commit bb5575cd27
18 changed files with 215 additions and 201 deletions

17
src/app/core/cache/cache.reducers.ts vendored Normal file
View File

@@ -0,0 +1,17 @@
import { combineReducers } from "@ngrx/store";
import { RequestCacheState, requestCacheReducer } from "./request-cache.reducer";
import { ObjectCacheState, objectCacheReducer } from "./object-cache.reducer";
export interface CacheState {
request: RequestCacheState,
object: ObjectCacheState
}
export const reducers = {
request: requestCacheReducer,
object: objectCacheReducer
};
export function cacheReducer(state: any, action: any) {
return combineReducers(reducers)(state, action);
}

View File

@@ -0,0 +1,33 @@
import { Action } from "@ngrx/store";
import { type } from "../../shared/ngrx/type";
import { CacheableObject } from "./object-cache.reducer";
export const ObjectCacheActionTypes = {
ADD: type('dspace/core/cache/object/ADD'),
REMOVE: type('dspace/core/cache/object/REMOVE')
};
export class AddToObjectCacheAction implements Action {
type = ObjectCacheActionTypes.ADD;
payload: {
objectToCache: CacheableObject;
msToLive: number;
};
constructor(objectToCache: CacheableObject, msToLive: number) {
this.payload = { objectToCache, msToLive };
}
}
export class RemoveFromObjectCacheAction implements Action {
type = ObjectCacheActionTypes.REMOVE;
payload: string;
constructor(uuid: string) {
this.payload = uuid;
}
}
export type ObjectCacheAction
= AddToObjectCacheAction
| RemoveFromObjectCacheAction

View File

@@ -0,0 +1,58 @@
import { ObjectCacheAction, ObjectCacheActionTypes, AddToObjectCacheAction, RemoveFromObjectCacheAction } from "./object-cache.actions";
import { hasValue } from "../../shared/empty.util";
export interface CacheableObject {
uuid: string;
}
export interface ObjectCacheEntry {
data: CacheableObject;
timeAdded: number;
msToLive: number;
}
export interface ObjectCacheState {
[uuid: string]: ObjectCacheEntry
}
// Object.create(null) ensures the object has no default js properties (e.g. `__proto__`)
const initialState: ObjectCacheState = Object.create(null);
export const objectCacheReducer = (state = initialState, action: ObjectCacheAction): ObjectCacheState => {
switch (action.type) {
case ObjectCacheActionTypes.ADD: {
return addToObjectCache(state, <AddToObjectCacheAction>action);
}
case ObjectCacheActionTypes.REMOVE: {
return removeFromObjectCache(state, <RemoveFromObjectCacheAction>action)
}
default: {
return state;
}
}
};
function addToObjectCache(state: ObjectCacheState, action: AddToObjectCacheAction): ObjectCacheState {
return Object.assign({}, state, {
[action.payload.objectToCache.uuid]: {
data: action.payload.objectToCache,
timeAdded: new Date().getTime(),
msToLive: action.payload.msToLive
}
});
}
function removeFromObjectCache(state: ObjectCacheState, action: RemoveFromObjectCacheAction): ObjectCacheState {
if (hasValue(state[action.payload])) {
let newObjectCache = Object.assign({}, state);
delete newObjectCache[action.payload];
return newObjectCache;
}
else {
return state;
}
}

View File

@@ -1,28 +1,28 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Store } from "@ngrx/store"; import { Store } from "@ngrx/store";
import { CacheState, CacheEntry, CacheableObject } from "./cache.reducer"; import { ObjectCacheState, ObjectCacheEntry, CacheableObject } from "./object-cache.reducer";
import { AddToCacheAction, RemoveFromCacheAction } from "./cache.actions"; import { AddToObjectCacheAction, RemoveFromObjectCacheAction } from "./object-cache.actions";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { hasNoValue } from "../../../shared/empty.util"; import { hasNoValue } from "../../shared/empty.util";
@Injectable() @Injectable()
export class CacheService { export class ObjectCacheService {
constructor( constructor(
private store: Store<CacheState> private store: Store<ObjectCacheState>
) {} ) {}
add(objectToCache: CacheableObject, msToLive: number): void { add(objectToCache: CacheableObject, msToLive: number): void {
this.store.dispatch(new AddToCacheAction(objectToCache, msToLive)); this.store.dispatch(new AddToObjectCacheAction(objectToCache, msToLive));
} }
remove(uuid: string): void { remove(uuid: string): void {
this.store.dispatch(new RemoveFromCacheAction(uuid)); this.store.dispatch(new RemoveFromObjectCacheAction(uuid));
} }
get<T extends CacheableObject>(uuid: string): Observable<T> { get<T extends CacheableObject>(uuid: string): Observable<T> {
return this.store.select<CacheEntry>('core', 'cache', uuid) return this.store.select<ObjectCacheEntry>('core', 'cache', 'object', uuid)
.filter(entry => this.isValid(entry)) .filter(entry => this.isValid(entry))
.map((entry: CacheEntry) => <T> entry.data); .map((entry: ObjectCacheEntry) => <T> entry.data);
} }
getList<T extends CacheableObject>(uuids: Array<string>): Observable<Array<T>> { getList<T extends CacheableObject>(uuids: Array<string>): Observable<Array<T>> {
@@ -34,14 +34,14 @@ export class CacheService {
has(uuid: string): boolean { has(uuid: string): boolean {
let result: boolean; let result: boolean;
this.store.select<CacheEntry>('core', 'cache', uuid) this.store.select<ObjectCacheEntry>('core', 'cache', 'object', uuid)
.take(1) .take(1)
.subscribe(entry => result = this.isValid(entry)); .subscribe(entry => result = this.isValid(entry));
return result; return result;
} }
private isValid(entry: CacheEntry): boolean { private isValid(entry: ObjectCacheEntry): boolean {
if (hasNoValue(entry)) { if (hasNoValue(entry)) {
return false; return false;
} }
@@ -49,7 +49,7 @@ export class CacheService {
const timeOutdated = entry.timeAdded + entry.msToLive; const timeOutdated = entry.timeAdded + entry.msToLive;
const isOutDated = new Date().getTime() > timeOutdated; const isOutDated = new Date().getTime() > timeOutdated;
if (isOutDated) { if (isOutDated) {
this.store.dispatch(new RemoveFromCacheAction(entry.data.uuid)); this.store.dispatch(new RemoveFromObjectCacheAction(entry.data.uuid));
} }
return !isOutDated; return !isOutDated;
} }

View File

@@ -4,15 +4,15 @@ import { type } from "../../shared/ngrx/type";
import { PaginationOptions } from "../shared/pagination-options.model"; import { PaginationOptions } from "../shared/pagination-options.model";
import { SortOptions } from "../shared/sort-options.model"; import { SortOptions } from "../shared/sort-options.model";
export const DataActionTypes = { export const RequestCacheActionTypes = {
FIND_BY_ID_REQUEST: type('dspace/core/data/FIND_BY_ID_REQUEST'), FIND_BY_ID_REQUEST: type('dspace/core/cache/request/FIND_BY_ID_REQUEST'),
FIND_ALL_REQUEST: type('dspace/core/data/FIND_ALL_REQUEST'), FIND_ALL_REQUEST: type('dspace/core/cache/request/FIND_ALL_REQUEST'),
SUCCESS: type('dspace/core/data/SUCCESS'), SUCCESS: type('dspace/core/cache/request/SUCCESS'),
ERROR: type('dspace/core/data/ERROR') ERROR: type('dspace/core/cache/request/ERROR')
}; };
export class DataFindAllRequestAction implements Action { export class FindAllRequestCacheAction implements Action {
type = DataActionTypes.FIND_ALL_REQUEST; type = RequestCacheActionTypes.FIND_ALL_REQUEST;
payload: { payload: {
key: string, key: string,
service: OpaqueToken, service: OpaqueToken,
@@ -38,8 +38,8 @@ export class DataFindAllRequestAction implements Action {
} }
} }
export class DataFindByIDRequestAction implements Action { export class FindByIDRequestCacheAction implements Action {
type = DataActionTypes.FIND_BY_ID_REQUEST; type = RequestCacheActionTypes.FIND_BY_ID_REQUEST;
payload: { payload: {
key: string, key: string,
service: OpaqueToken, service: OpaqueToken,
@@ -59,8 +59,8 @@ export class DataFindByIDRequestAction implements Action {
} }
} }
export class DataSuccessAction implements Action { export class RequestCacheSuccessAction implements Action {
type = DataActionTypes.SUCCESS; type = RequestCacheActionTypes.SUCCESS;
payload: { payload: {
key: string, key: string,
resourceUUIDs: Array<string> resourceUUIDs: Array<string>
@@ -74,8 +74,8 @@ export class DataSuccessAction implements Action {
} }
} }
export class DataErrorAction implements Action { export class RequestCacheErrorAction implements Action {
type = DataActionTypes.ERROR; type = RequestCacheActionTypes.ERROR;
payload: { payload: {
key: string, key: string,
errorMessage: string errorMessage: string
@@ -89,8 +89,8 @@ export class DataErrorAction implements Action {
} }
} }
export type DataAction export type RequestCacheAction
= DataFindAllRequestAction = FindAllRequestCacheAction
| DataFindByIDRequestAction | FindByIDRequestCacheAction
| DataSuccessAction | RequestCacheSuccessAction
| DataErrorAction; | RequestCacheErrorAction;

View File

@@ -1,12 +1,12 @@
import { PaginationOptions } from "../shared/pagination-options.model"; import { PaginationOptions } from "../shared/pagination-options.model";
import { SortOptions } from "../shared/sort-options.model"; import { SortOptions } from "../shared/sort-options.model";
import { import {
DataAction, DataActionTypes, DataFindAllRequestAction, RequestCacheAction, RequestCacheActionTypes, FindAllRequestCacheAction,
DataSuccessAction, DataErrorAction, DataFindByIDRequestAction RequestCacheSuccessAction, RequestCacheErrorAction, FindByIDRequestCacheAction
} from "./data.actions"; } from "./request-cache.actions";
import { OpaqueToken } from "@angular/core"; import { OpaqueToken } from "@angular/core";
export interface DataRequestState { export interface CachedRequest {
service: OpaqueToken service: OpaqueToken
scopeID: string; scopeID: string;
resourceID: string; resourceID: string;
@@ -20,30 +20,30 @@ export interface DataRequestState {
msToLive: number; msToLive: number;
} }
export interface DataState { export interface RequestCacheState {
[key: string]: DataRequestState [key: string]: CachedRequest
} }
// Object.create(null) ensures the object has no default js properties (e.g. `__proto__`) // Object.create(null) ensures the object has no default js properties (e.g. `__proto__`)
const initialState = Object.create(null); const initialState = Object.create(null);
export const dataReducer = (state = initialState, action: DataAction): DataState => { export const requestCacheReducer = (state = initialState, action: RequestCacheAction): RequestCacheState => {
switch (action.type) { switch (action.type) {
case DataActionTypes.FIND_ALL_REQUEST: { case RequestCacheActionTypes.FIND_ALL_REQUEST: {
return findAllRequest(state, <DataFindAllRequestAction> action); return findAllRequest(state, <FindAllRequestCacheAction> action);
} }
case DataActionTypes.FIND_BY_ID_REQUEST: { case RequestCacheActionTypes.FIND_BY_ID_REQUEST: {
return findByIDRequest(state, <DataFindByIDRequestAction> action); return findByIDRequest(state, <FindByIDRequestCacheAction> action);
} }
case DataActionTypes.SUCCESS: { case RequestCacheActionTypes.SUCCESS: {
return success(state, <DataSuccessAction> action); return success(state, <RequestCacheSuccessAction> action);
} }
case DataActionTypes.ERROR: { case RequestCacheActionTypes.ERROR: {
return error(state, <DataErrorAction> action); return error(state, <RequestCacheErrorAction> action);
} }
default: { default: {
@@ -52,7 +52,7 @@ export const dataReducer = (state = initialState, action: DataAction): DataState
} }
}; };
function findAllRequest(state: DataState, action: DataFindAllRequestAction): DataState { function findAllRequest(state: RequestCacheState, action: FindAllRequestCacheAction): RequestCacheState {
return Object.assign({}, state, { return Object.assign({}, state, {
[action.payload.key]: { [action.payload.key]: {
service: action.payload.service, service: action.payload.service,
@@ -66,7 +66,7 @@ function findAllRequest(state: DataState, action: DataFindAllRequestAction): Dat
}); });
} }
function findByIDRequest(state: DataState, action: DataFindByIDRequestAction): DataState { function findByIDRequest(state: RequestCacheState, action: FindByIDRequestCacheAction): RequestCacheState {
return Object.assign({}, state, { return Object.assign({}, state, {
[action.payload.key]: { [action.payload.key]: {
service: action.payload.service, service: action.payload.service,
@@ -78,7 +78,7 @@ function findByIDRequest(state: DataState, action: DataFindByIDRequestAction): D
}); });
} }
function success(state: DataState, action: DataSuccessAction): DataState { function success(state: RequestCacheState, action: RequestCacheSuccessAction): RequestCacheState {
return Object.assign({}, state, { return Object.assign({}, state, {
[action.payload.key]: Object.assign({}, state[action.payload.key], { [action.payload.key]: Object.assign({}, state[action.payload.key], {
isLoading: false, isLoading: false,
@@ -88,7 +88,7 @@ function success(state: DataState, action: DataSuccessAction): DataState {
}); });
} }
function error(state: DataState, action: DataErrorAction): DataState { function error(state: RequestCacheState, action: RequestCacheErrorAction): RequestCacheState {
return Object.assign({}, state, { return Object.assign({}, state, {
[action.payload.key]: Object.assign({}, state[action.payload.key], { [action.payload.key]: Object.assign({}, state[action.payload.key], {
isLoading: false, isLoading: false,

View File

@@ -4,7 +4,7 @@ import { SharedModule } from "../shared/shared.module";
import { isNotEmpty } from "../shared/empty.util"; import { isNotEmpty } from "../shared/empty.util";
import { FooterComponent } from "./footer/footer.component"; import { FooterComponent } from "./footer/footer.component";
import { DSpaceRESTv2Service } from "./dspace-rest-v2/dspace-rest-v2.service"; import { DSpaceRESTv2Service } from "./dspace-rest-v2/dspace-rest-v2.service";
import { CacheService } from "./data-services/cache/cache.service"; import { ObjectCacheService } from "./cache/object-cache.service";
import { CollectionDataService } from "./data-services/collection-data.service"; import { CollectionDataService } from "./data-services/collection-data.service";
import { ItemDataService } from "./data-services/item-data.service"; import { ItemDataService } from "./data-services/item-data.service";
@@ -25,7 +25,7 @@ const PROVIDERS = [
CollectionDataService, CollectionDataService,
ItemDataService, ItemDataService,
DSpaceRESTv2Service, DSpaceRESTv2Service,
CacheService ObjectCacheService
]; ];
@NgModule({ @NgModule({

View File

@@ -1,14 +1,11 @@
import { combineReducers } from "@ngrx/store"; import { combineReducers } from "@ngrx/store";
import { CacheState, cacheReducer } from "./data-services/cache/cache.reducer"; import { CacheState, cacheReducer } from "./cache/cache.reducers";
import { dataReducer, DataState } from "./data-services/data.reducer";
export interface CoreState { export interface CoreState {
data: DataState,
cache: CacheState cache: CacheState
} }
export const reducers = { export const reducers = {
data: dataReducer,
cache: cacheReducer cache: cacheReducer
}; };

View File

@@ -1,33 +0,0 @@
import { Action } from "@ngrx/store";
import { type } from "../../../shared/ngrx/type";
import { CacheableObject } from "./cache.reducer";
export const CacheActionTypes = {
ADD: type('dspace/core/data/cache/ADD'),
REMOVE: type('dspace/core/data/cache/REMOVE')
};
export class AddToCacheAction implements Action {
type = CacheActionTypes.ADD;
payload: {
objectToCache: CacheableObject;
msToLive: number;
};
constructor(objectToCache: CacheableObject, msToLive: number) {
this.payload = { objectToCache, msToLive };
}
}
export class RemoveFromCacheAction implements Action {
type = CacheActionTypes.REMOVE;
payload: string;
constructor(uuid: string) {
this.payload = uuid;
}
}
export type CacheAction
= AddToCacheAction
| RemoveFromCacheAction

View File

@@ -1,58 +0,0 @@
import { CacheAction, CacheActionTypes, AddToCacheAction, RemoveFromCacheAction } from "./cache.actions";
import { hasValue } from "../../../shared/empty.util";
export interface CacheableObject {
uuid: string;
}
export interface CacheEntry {
data: CacheableObject;
timeAdded: number;
msToLive: number;
}
export interface CacheState {
[uuid: string]: CacheEntry
}
// Object.create(null) ensures the object has no default js properties (e.g. `__proto__`)
const initialState: CacheState = Object.create(null);
export const cacheReducer = (state = initialState, action: CacheAction): CacheState => {
switch (action.type) {
case CacheActionTypes.ADD: {
return addToCache(state, <AddToCacheAction>action);
}
case CacheActionTypes.REMOVE: {
return removeFromCache(state, <RemoveFromCacheAction>action)
}
default: {
return state;
}
}
};
function addToCache(state: CacheState, action: AddToCacheAction): CacheState {
return Object.assign({}, state, {
[action.payload.objectToCache.uuid]: {
data: action.payload.objectToCache,
timeAdded: new Date().getTime(),
msToLive: action.payload.msToLive
}
});
}
function removeFromCache(state: CacheState, action: RemoveFromCacheAction): CacheState {
if (hasValue(state[action.payload])) {
let newCache = Object.assign({}, state);
delete newCache[action.payload];
return newCache;
}
else {
return state;
}
}

View File

@@ -3,10 +3,10 @@ import { DataEffects } from "./data.effects";
import { Serializer } from "../serializer"; import { Serializer } from "../serializer";
import { Collection } from "../shared/collection.model"; import { Collection } from "../shared/collection.model";
import { DSpaceRESTv2Serializer } from "../dspace-rest-v2/dspace-rest-v2.serializer"; import { DSpaceRESTv2Serializer } from "../dspace-rest-v2/dspace-rest-v2.serializer";
import { CacheService } from "./cache/cache.service"; import { ObjectCacheService } from "../cache/object-cache.service";
import { DSpaceRESTv2Service } from "../dspace-rest-v2/dspace-rest-v2.service"; import { DSpaceRESTv2Service } from "../dspace-rest-v2/dspace-rest-v2.service";
import { Actions, Effect } from "@ngrx/effects"; import { Actions, Effect } from "@ngrx/effects";
import { DataFindAllRequestAction, DataFindByIDRequestAction } from "./data.actions"; import { FindAllRequestCacheAction, FindByIDRequestCacheAction } from "../cache/request-cache.actions";
import { CollectionDataService } from "./collection-data.service"; import { CollectionDataService } from "./collection-data.service";
@Injectable() @Injectable()
@@ -14,17 +14,17 @@ export class CollectionDataEffects extends DataEffects<Collection> {
constructor( constructor(
actions$: Actions, actions$: Actions,
restApi: DSpaceRESTv2Service, restApi: DSpaceRESTv2Service,
cache: CacheService, cache: ObjectCacheService,
dataService: CollectionDataService dataService: CollectionDataService
) { ) {
super(actions$, restApi, cache, dataService); super(actions$, restApi, cache, dataService);
} }
protected getFindAllEndpoint(action: DataFindAllRequestAction): string { protected getFindAllEndpoint(action: FindAllRequestCacheAction): string {
return '/collections'; return '/collections';
} }
protected getFindByIdEndpoint(action: DataFindByIDRequestAction): string { protected getFindByIdEndpoint(action: FindByIDRequestCacheAction): string {
return `/collections/${action.payload.resourceID}`; return `/collections/${action.payload.resourceID}`;
} }

View File

@@ -2,16 +2,16 @@ import { Injectable, OpaqueToken } from "@angular/core";
import { Store } from "@ngrx/store"; import { Store } from "@ngrx/store";
import { DataService } from "./data.service"; import { DataService } from "./data.service";
import { Collection } from "../shared/collection.model"; import { Collection } from "../shared/collection.model";
import { CacheService } from "./cache/cache.service"; import { ObjectCacheService } from "../cache/object-cache.service";
import { DataState } from "./data.reducer"; import { RequestCacheState } from "../cache/request-cache.reducer";
@Injectable() @Injectable()
export class CollectionDataService extends DataService<Collection> { export class CollectionDataService extends DataService<Collection> {
name = new OpaqueToken('CollectionDataService'); name = new OpaqueToken('CollectionDataService');
constructor( constructor(
store: Store<DataState>, store: Store<RequestCacheState>,
cache: CacheService cache: ObjectCacheService
) { ) {
super(store, cache); super(store, cache);
} }

View File

@@ -2,25 +2,25 @@ import { Actions, Effect } from "@ngrx/effects";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { DSpaceRESTV2Response } from "../dspace-rest-v2/dspace-rest-v2-response.model"; import { DSpaceRESTV2Response } from "../dspace-rest-v2/dspace-rest-v2-response.model";
import { DSpaceRESTv2Service } from "../dspace-rest-v2/dspace-rest-v2.service"; import { DSpaceRESTv2Service } from "../dspace-rest-v2/dspace-rest-v2.service";
import { CacheService } from "./cache/cache.service"; import { ObjectCacheService } from "../cache/object-cache.service";
import { GlobalConfig } from "../../../config"; import { GlobalConfig } from "../../../config";
import { CacheableObject } from "./cache/cache.reducer"; import { CacheableObject } from "../cache/object-cache.reducer";
import { Serializer } from "../serializer"; import { Serializer } from "../serializer";
import { import {
DataActionTypes, DataFindAllRequestAction, DataSuccessAction, RequestCacheActionTypes, FindAllRequestCacheAction, RequestCacheSuccessAction,
DataErrorAction, DataFindByIDRequestAction, DataAction RequestCacheErrorAction, FindByIDRequestCacheAction
} from "./data.actions"; } from "../cache/request-cache.actions";
import { DataService } from "./data.service"; import { DataService } from "./data.service";
export abstract class DataEffects<T extends CacheableObject> { export abstract class DataEffects<T extends CacheableObject> {
protected abstract getFindAllEndpoint(action: DataFindAllRequestAction): string; protected abstract getFindAllEndpoint(action: FindAllRequestCacheAction): string;
protected abstract getFindByIdEndpoint(action: DataFindByIDRequestAction): string; protected abstract getFindByIdEndpoint(action: FindByIDRequestCacheAction): string;
protected abstract getSerializer(): Serializer<T>; protected abstract getSerializer(): Serializer<T>;
constructor( constructor(
private actions$: Actions, private actions$: Actions,
private restApi: DSpaceRESTv2Service, private restApi: DSpaceRESTv2Service,
private cache: CacheService, private objectCache: ObjectCacheService,
private dataService: DataService<T> private dataService: DataService<T>
) {} ) {}
@@ -28,33 +28,33 @@ export abstract class DataEffects<T extends CacheableObject> {
// because currently the cache is more of an object store. We need to move // because currently the cache is more of an object store. We need to move
// more towards memoization for things like this. // more towards memoization for things like this.
protected findAll = this.actions$ protected findAll = this.actions$
.ofType(DataActionTypes.FIND_ALL_REQUEST) .ofType(RequestCacheActionTypes.FIND_ALL_REQUEST)
.filter((action: DataFindAllRequestAction) => action.payload.service === this.dataService.name) .filter((action: FindAllRequestCacheAction) => action.payload.service === this.dataService.name)
.switchMap((action: DataFindAllRequestAction) => { .switchMap((action: FindAllRequestCacheAction) => {
//TODO scope, pagination, sorting -> when we know how that works in rest //TODO scope, pagination, sorting -> when we know how that works in rest
return this.restApi.get(this.getFindAllEndpoint(action)) return this.restApi.get(this.getFindAllEndpoint(action))
.map((data: DSpaceRESTV2Response) => this.getSerializer().deserializeArray(data)) .map((data: DSpaceRESTV2Response) => this.getSerializer().deserializeArray(data))
.do((ts: T[]) => { .do((ts: T[]) => {
ts.forEach((t) => { ts.forEach((t) => {
this.cache.add(t, GlobalConfig.cache.msToLive); this.objectCache.add(t, GlobalConfig.cache.msToLive);
}); });
}) })
.map((ts: Array<T>) => ts.map(t => t.uuid)) .map((ts: Array<T>) => ts.map(t => t.uuid))
.map((ids: Array<string>) => new DataSuccessAction(action.payload.key, ids)) .map((ids: Array<string>) => new RequestCacheSuccessAction(action.payload.key, ids))
.catch((errorMsg: string) => Observable.of(new DataErrorAction(action.payload.key, errorMsg))); .catch((errorMsg: string) => Observable.of(new RequestCacheErrorAction(action.payload.key, errorMsg)));
}); });
protected findById = this.actions$ protected findById = this.actions$
.ofType(DataActionTypes.FIND_BY_ID_REQUEST) .ofType(RequestCacheActionTypes.FIND_BY_ID_REQUEST)
.filter((action: DataFindAllRequestAction) => action.payload.service === this.dataService.name) .filter((action: FindAllRequestCacheAction) => action.payload.service === this.dataService.name)
.switchMap((action: DataFindByIDRequestAction) => { .switchMap((action: FindByIDRequestCacheAction) => {
return this.restApi.get(this.getFindByIdEndpoint(action)) return this.restApi.get(this.getFindByIdEndpoint(action))
.map((data: DSpaceRESTV2Response) => this.getSerializer().deserialize(data)) .map((data: DSpaceRESTV2Response) => this.getSerializer().deserialize(data))
.do((t: T) => { .do((t: T) => {
this.cache.add(t, GlobalConfig.cache.msToLive); this.objectCache.add(t, GlobalConfig.cache.msToLive);
}) })
.map((t: T) => new DataSuccessAction(action.payload.key, [t.uuid])) .map((t: T) => new RequestCacheSuccessAction(action.payload.key, [t.uuid]))
.catch((errorMsg: string) => Observable.of(new DataErrorAction(action.payload.key, errorMsg))); .catch((errorMsg: string) => Observable.of(new RequestCacheErrorAction(action.payload.key, errorMsg)));
}); });
} }

View File

@@ -1,10 +1,10 @@
import { OpaqueToken } from "@angular/core"; import { OpaqueToken } from "@angular/core";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { Store } from "@ngrx/store"; import { Store } from "@ngrx/store";
import { CacheService } from "./cache/cache.service"; import { ObjectCacheService } from "../cache/object-cache.service";
import { CacheableObject } from "./cache/cache.reducer"; import { CacheableObject } from "../cache/object-cache.reducer";
import { DataState } from "./data.reducer"; import { RequestCacheState } from "../cache/request-cache.reducer";
import { DataFindAllRequestAction, DataFindByIDRequestAction } from "./data.actions"; import { FindAllRequestCacheAction, FindByIDRequestCacheAction } from "../cache/request-cache.actions";
import { ParamHash } from "../shared/param-hash"; import { ParamHash } from "../shared/param-hash";
import { isNotEmpty } from "../../shared/empty.util"; import { isNotEmpty } from "../../shared/empty.util";
@@ -12,28 +12,28 @@ export abstract class DataService<T extends CacheableObject> {
abstract name: OpaqueToken; abstract name: OpaqueToken;
constructor( constructor(
private store: Store<DataState>, private store: Store<RequestCacheState>,
private cache: CacheService private objectCache: ObjectCacheService
) { } ) { }
findAll(scopeID?: string): Observable<Array<T>> { findAll(scopeID?: string): Observable<Array<T>> {
const key = new ParamHash(this.name, 'findAll', scopeID).toString(); const key = new ParamHash(this.name, 'findAll', scopeID).toString();
this.store.dispatch(new DataFindAllRequestAction(key, this.name, scopeID)); this.store.dispatch(new FindAllRequestCacheAction(key, this.name, scopeID));
//get an observable of the IDs from the store //get an observable of the IDs from the store
return this.store.select<Array<string>>('core', 'data', key, 'resourceUUIDs') return this.store.select<Array<string>>('core', 'cache', 'request', key, 'resourceUUIDs')
.flatMap((resourceUUIDs: Array<string>) => { .flatMap((resourceUUIDs: Array<string>) => {
// use those IDs to fetch the actual objects from the cache // use those IDs to fetch the actual objects from the cache
return this.cache.getList<T>(resourceUUIDs); return this.objectCache.getList<T>(resourceUUIDs);
}); });
} }
findById(id: string): Observable<T> { findById(id: string): Observable<T> {
const key = new ParamHash(this.name, 'findById', id).toString(); const key = new ParamHash(this.name, 'findById', id).toString();
this.store.dispatch(new DataFindByIDRequestAction(key, this.name, id)); this.store.dispatch(new FindByIDRequestCacheAction(key, this.name, id));
return this.store.select<Array<string>>('core', 'data', key, 'resourceUUIDs') return this.store.select<Array<string>>('core', 'cache', 'request', key, 'resourceUUIDs')
.flatMap((resourceUUIDs: Array<string>) => { .flatMap((resourceUUIDs: Array<string>) => {
if(isNotEmpty(resourceUUIDs)) { if(isNotEmpty(resourceUUIDs)) {
return this.cache.get<T>(resourceUUIDs[0]); return this.objectCache.get<T>(resourceUUIDs[0]);
} }
else { else {
return Observable.of(undefined); return Observable.of(undefined);

View File

@@ -3,10 +3,10 @@ import { DataEffects } from "./data.effects";
import { Serializer } from "../serializer"; import { Serializer } from "../serializer";
import { Item } from "../shared/item.model"; import { Item } from "../shared/item.model";
import { DSpaceRESTv2Serializer } from "../dspace-rest-v2/dspace-rest-v2.serializer"; import { DSpaceRESTv2Serializer } from "../dspace-rest-v2/dspace-rest-v2.serializer";
import { CacheService } from "./cache/cache.service"; import { ObjectCacheService } from "../cache/object-cache.service";
import { DSpaceRESTv2Service } from "../dspace-rest-v2/dspace-rest-v2.service"; import { DSpaceRESTv2Service } from "../dspace-rest-v2/dspace-rest-v2.service";
import { Actions, Effect } from "@ngrx/effects"; import { Actions, Effect } from "@ngrx/effects";
import { DataFindAllRequestAction, DataFindByIDRequestAction } from "./data.actions"; import { FindAllRequestCacheAction, FindByIDRequestCacheAction } from "../cache/request-cache.actions";
import { ItemDataService } from "./item-data.service"; import { ItemDataService } from "./item-data.service";
@Injectable() @Injectable()
@@ -14,17 +14,17 @@ export class ItemDataEffects extends DataEffects<Item> {
constructor( constructor(
actions$: Actions, actions$: Actions,
restApi: DSpaceRESTv2Service, restApi: DSpaceRESTv2Service,
cache: CacheService, cache: ObjectCacheService,
dataService: ItemDataService dataService: ItemDataService
) { ) {
super(actions$, restApi, cache, dataService); super(actions$, restApi, cache, dataService);
} }
protected getFindAllEndpoint(action: DataFindAllRequestAction): string { protected getFindAllEndpoint(action: FindAllRequestCacheAction): string {
return '/items'; return '/items';
} }
protected getFindByIdEndpoint(action: DataFindByIDRequestAction): string { protected getFindByIdEndpoint(action: FindByIDRequestCacheAction): string {
return `/items/${action.payload.resourceID}`; return `/items/${action.payload.resourceID}`;
} }

View File

@@ -2,16 +2,16 @@ import { Injectable, OpaqueToken } from "@angular/core";
import { Store } from "@ngrx/store"; import { Store } from "@ngrx/store";
import { DataService } from "./data.service"; import { DataService } from "./data.service";
import { Item } from "../shared/item.model"; import { Item } from "../shared/item.model";
import { CacheService } from "./cache/cache.service"; import { ObjectCacheService } from "../cache/object-cache.service";
import { DataState } from "./data.reducer"; import { RequestCacheState } from "../cache/request-cache.reducer";
@Injectable() @Injectable()
export class ItemDataService extends DataService<Item> { export class ItemDataService extends DataService<Item> {
name = new OpaqueToken('ItemDataService'); name = new OpaqueToken('ItemDataService');
constructor( constructor(
store: Store<DataState>, store: Store<RequestCacheState>,
cache: CacheService cache: ObjectCacheService
) { ) {
super(store, cache); super(store, cache);
} }

View File

@@ -1,7 +1,7 @@
import { autoserialize, autoserializeAs } from "cerialize"; import { autoserialize, autoserializeAs } from "cerialize";
import { Metadatum } from "./metadatum.model" import { Metadatum } from "./metadatum.model"
import { isEmpty, isNotEmpty } from "../../shared/empty.util"; import { isEmpty, isNotEmpty } from "../../shared/empty.util";
import { CacheableObject } from "../data-services/cache/cache.reducer"; import { CacheableObject } from "../cache/object-cache.reducer";
/** /**
* An abstract model class for a DSpaceObject. * An abstract model class for a DSpaceObject.

View File

@@ -92,7 +92,7 @@ export function createMockApi() {
router.route('/collections/:collection_id') router.route('/collections/:collection_id')
.get(function(req, res) { .get(function(req, res) {
console.log('GET', util.inspect(req.collection.id, { colors: true })); // console.log('GET', util.inspect(req.collection.id, { colors: true }));
res.json(toHALResponse(req, req.collection)); res.json(toHALResponse(req, req.collection));
// }) // })
// .put(function(req, res) { // .put(function(req, res) {
@@ -154,7 +154,7 @@ export function createMockApi() {
router.route('/items/:item_id') router.route('/items/:item_id')
.get(function(req, res) { .get(function(req, res) {
console.log('GET', util.inspect(req.item, { colors: true })); // console.log('GET', util.inspect(req.item, { colors: true }));
res.json(toHALResponse(req, req.item)); res.json(toHALResponse(req, req.item));
// }) // })
// .put(function(req, res) { // .put(function(req, res) {
@@ -199,7 +199,7 @@ export function createMockApi() {
router.route('/bundles/:bundle_id') router.route('/bundles/:bundle_id')
.get(function(req, res) { .get(function(req, res) {
console.log('GET', util.inspect(req.bundle, { colors: true })); // console.log('GET', util.inspect(req.bundle, { colors: true }));
res.json(toHALResponse(req, req.bundle)); res.json(toHALResponse(req, req.bundle));
}); });
@@ -229,7 +229,7 @@ export function createMockApi() {
router.route('/bitstreams/:bitstream_id') router.route('/bitstreams/:bitstream_id')
.get(function(req, res) { .get(function(req, res) {
console.log('GET', util.inspect(req.bitstream, { colors: true })); // console.log('GET', util.inspect(req.bitstream, { colors: true }));
res.json(toHALResponse(req, req.bitstream)); res.json(toHALResponse(req, req.bitstream));
}); });