diff --git a/package.json b/package.json index dca2420c62..c725029a12 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,8 @@ "server:watch": "nodemon dist/server.js", "server:watch:debug": "nodemon --debug dist/server.js", "webpack:watch": "webpack -w --mode development", - "watch": "yarn run build && npm-run-all -p webpack:watch server:watch --mode development", - "watch:debug": "yarn run build && npm-run-all -p webpack:watch server:watch:debug --mode development", + "watch": "yarn run build && npm-run-all -p webpack:watch server:watch", + "watch:debug": "yarn run build && npm-run-all -p webpack:watch server:watch:debug", "predebug": "yarn run build", "predebug:server": "yarn run build", "debug": "node --debug-brk dist/server.js", @@ -96,6 +96,7 @@ "core-js": "2.5.3", "express": "4.16.2", "express-session": "1.15.6", + "fast-json-patch": "^2.0.7", "font-awesome": "4.7.0", "http-server": "0.11.1", "https": "1.0.0", diff --git a/src/app/core/cache/object-cache.actions.ts b/src/app/core/cache/object-cache.actions.ts index a136b04248..957c0f7269 100644 --- a/src/app/core/cache/object-cache.actions.ts +++ b/src/app/core/cache/object-cache.actions.ts @@ -2,6 +2,7 @@ import { Action } from '@ngrx/store'; import { type } from '../../shared/ngrx/type'; import { CacheableObject } from './object-cache.reducer'; +import { Operation } from 'fast-json-patch'; /** * The list of ObjectCacheAction type definitions @@ -9,7 +10,8 @@ import { CacheableObject } from './object-cache.reducer'; export const ObjectCacheActionTypes = { ADD: type('dspace/core/cache/object/ADD'), REMOVE: type('dspace/core/cache/object/REMOVE'), - RESET_TIMESTAMPS: type('dspace/core/cache/object/RESET_TIMESTAMPS') + RESET_TIMESTAMPS: type('dspace/core/cache/object/RESET_TIMESTAMPS'), + PATCH: type('dspace/core/cache/object/PATCH') }; /* tslint:disable:max-classes-per-file */ @@ -79,6 +81,30 @@ export class ResetObjectCacheTimestampsAction implements Action { this.payload = newTimestamp; } } + +/** + * An ngrx action to add new operations to a specified cached objects + */ +export class PatchObjectCacheAction implements Action { + type = ObjectCacheActionTypes.PATCH; + payload: { + uuid: string, + operations: Operation[] + }; + + /** + * Create a new PatchObjectCacheAction + * + * @param uuid + * the uuid of the object that should be updated + * @param operations + * the list of operations to add + */ + constructor(uuid: string, operations: Operation[]) { + this.payload = { uuid, operations }; + } +} + /* tslint:enable:max-classes-per-file */ /** @@ -87,4 +113,5 @@ export class ResetObjectCacheTimestampsAction implements Action { export type ObjectCacheAction = AddToObjectCacheAction | RemoveFromObjectCacheAction - | ResetObjectCacheTimestampsAction; + | ResetObjectCacheTimestampsAction + | PatchObjectCacheAction; diff --git a/src/app/core/cache/object-cache.reducer.ts b/src/app/core/cache/object-cache.reducer.ts index 3a1830e14a..5cc4b0551a 100644 --- a/src/app/core/cache/object-cache.reducer.ts +++ b/src/app/core/cache/object-cache.reducer.ts @@ -1,10 +1,11 @@ import { ObjectCacheAction, ObjectCacheActionTypes, AddToObjectCacheAction, - RemoveFromObjectCacheAction, ResetObjectCacheTimestampsAction + RemoveFromObjectCacheAction, ResetObjectCacheTimestampsAction, PatchObjectCacheAction } from './object-cache.actions'; -import { hasValue } from '../../shared/empty.util'; +import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { CacheEntry } from './cache-entry'; import { ResourceType } from '../shared/resource-type'; +import { Operation } from 'fast-json-patch'; export enum DirtyType { Created = 'Created', @@ -36,6 +37,7 @@ export class ObjectCacheEntry implements CacheEntry { timeAdded: number; msToLive: number; requestHref: string; + operations: Operation[]; } /** @@ -76,6 +78,9 @@ export function objectCacheReducer(state = initialState, action: ObjectCacheActi return resetObjectCacheTimestamps(state, action as ResetObjectCacheTimestampsAction) } + case ObjectCacheActionTypes.PATCH: { + return patchObjectCache(state, action as PatchObjectCacheAction); + } default: { return state; } @@ -98,7 +103,8 @@ function addToObjectCache(state: ObjectCacheState, action: AddToObjectCacheActio data: action.payload.objectToCache, timeAdded: action.payload.timeAdded, msToLive: action.payload.msToLive, - requestHref: action.payload.requestHref + requestHref: action.payload.requestHref, + operations: [] } }); } @@ -143,3 +149,23 @@ function resetObjectCacheTimestamps(state: ObjectCacheState, action: ResetObject }); return newState; } + +/** + * Add the list of patch operations to a cached object + * + * @param state + * the current state + * @param action + * a PatchObjectCacheAction + * @return ObjectCacheState + * the new state, with the new operations added to the state of the specified ObjectCacheEntry + */ +function patchObjectCache(state: ObjectCacheState, action: PatchObjectCacheAction): ObjectCacheState { + const uuid = action.payload.uuid; + const operations = action.payload.operations; + const newState = Object.assign({}, state); + if (hasValue(newState[uuid])) { + newState[uuid].operations = state[uuid].operations.concat(operations); + } + return newState; +} diff --git a/src/app/core/cache/object-cache.service.ts b/src/app/core/cache/object-cache.service.ts index dbe241ffb3..d9b3077d55 100644 --- a/src/app/core/cache/object-cache.service.ts +++ b/src/app/core/cache/object-cache.service.ts @@ -1,18 +1,23 @@ import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; -import { distinctUntilChanged, filter, first, map, mergeMap, take } from 'rxjs/operators'; +import { distinctUntilChanged, filter, first, map, mergeMap, take, tap } from 'rxjs/operators'; import { Injectable } from '@angular/core'; import { MemoizedSelector, select, Store } from '@ngrx/store'; import { IndexName } from '../index/index.reducer'; import { CacheableObject, ObjectCacheEntry } from './object-cache.reducer'; -import { AddToObjectCacheAction, RemoveFromObjectCacheAction } from './object-cache.actions'; -import { hasNoValue } from '../../shared/empty.util'; +import { + AddToObjectCacheAction, + PatchObjectCacheAction, + RemoveFromObjectCacheAction +} from './object-cache.actions'; +import { hasNoValue, isNotEmpty } from '../../shared/empty.util'; import { GenericConstructor } from '../shared/generic-constructor'; import { coreSelector, CoreState } from '../core.reducers'; import { pathSelector } from '../shared/selectors'; import { NormalizedObjectFactory } from './models/normalized-object-factory'; import { NormalizedObject } from './models/normalized-object.model'; +import { applyPatch, Operation } from 'fast-json-patch'; function selfLinkFromUuidSelector(uuid: string): MemoizedSelector { return pathSelector(coreSelector, 'index', IndexName.OBJECT, uuid); @@ -85,7 +90,11 @@ export class ObjectCacheService { map((entry: ObjectCacheEntry) => { const type: GenericConstructor = NormalizedObjectFactory.getConstructor(entry.data.type); return Object.assign(new type(), entry.data) as T - })); + }), + // map((entry: ObjectCacheEntry) => + // applyPatch(entry.data, entry.operations).newDocument + // ) + ); } private getEntry(selfLink: string): Observable { @@ -195,4 +204,26 @@ export class ObjectCacheService { } } + /** + * Add operations to a the existing list of operations for an ObjectCacheEntry + * @param {string} uuid + * the uuid of the ObjectCacheEntry + * @param {Operation[]} patch + * list of operations to perform + */ + private addOperations(uuid: string, patch: Operation[]) { + this.store.dispatch(new PatchObjectCacheAction(uuid, patch)); + } + + /** + * Check whether there are any unperformed operations for an ObjectCacheEntry + * + * @param entry + * the entry to check + * @return boolean + * false if the entry is there are no operations left in the ObjectCacheEntry, true otherwise + */ + private isDirty(entry: ObjectCacheEntry): boolean { + return isNotEmpty(entry.operations); + } } diff --git a/src/app/shared/form/form.reducer.ts b/src/app/shared/form/form.reducer.ts index 1a5d2da30b..eea5f99f20 100644 --- a/src/app/shared/form/form.reducer.ts +++ b/src/app/shared/form/form.reducer.ts @@ -30,7 +30,6 @@ export interface FormState { const initialState: FormState = Object.create(null); export function formReducer(state = initialState, action: FormAction): FormState { - console.log('TEST'); switch (action.type) { case FormActionTypes.FORM_INIT: { diff --git a/yarn.lock b/yarn.lock index 6129ef4e7e..b834144eb8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3026,6 +3026,12 @@ fast-glob@^2.0.2: merge2 "^1.2.1" micromatch "^3.1.10" +fast-json-patch@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-2.0.7.tgz#55864b08b1e50381d2f37fd472bb2e18fe54a733" + dependencies: + deep-equal "^1.0.1" + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"