1
0

65717: Move operations from custom order + sending bundle patch

This commit is contained in:
Kristof De Langhe
2019-10-28 14:27:12 +01:00
parent b550667bc9
commit 8059301906
5 changed files with 78 additions and 5 deletions

View File

@@ -24,6 +24,9 @@ import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-o
import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer'; import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
import { Bitstream } from '../../../core/shared/bitstream.model'; import { Bitstream } from '../../../core/shared/bitstream.model';
import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions'; import { FieldChangeType } from '../../../core/data/object-updates/object-updates.actions';
import { Operation } from 'fast-json-patch';
import { MoveOperation } from 'fast-json-patch/lib/core';
import { BundleDataService } from '../../../core/data/bundle-data.service';
@Component({ @Component({
selector: 'ds-item-bitstreams', selector: 'ds-item-bitstreams',
@@ -66,7 +69,8 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
public bitstreamService: BitstreamDataService, public bitstreamService: BitstreamDataService,
public objectCache: ObjectCacheService, public objectCache: ObjectCacheService,
public requestService: RequestService, public requestService: RequestService,
public cdRef: ChangeDetectorRef public cdRef: ChangeDetectorRef,
public bundleService: BundleDataService
) { ) {
super(itemService, objectUpdatesService, router, notificationsService, translateService, EnvConfig, route); super(itemService, objectUpdatesService, router, notificationsService, translateService, EnvConfig, route);
} }
@@ -141,6 +145,19 @@ export class ItemBitstreamsComponent extends AbstractItemUpdateComponent impleme
this.displayNotifications(responses); this.displayNotifications(responses);
this.reset(); this.reset();
}); });
this.bundles$.pipe(take(1)).subscribe((bundles: Bundle[]) => {
bundles.forEach((bundle: Bundle) => {
this.objectUpdatesService.getMoveOperations(bundle.self).pipe(
take(1),
isNotEmptyOperator(),
map((operations: MoveOperation[]) => [...operations.map((operation: MoveOperation) => Object.assign(operation, {
from: `/_links/bitstreams${operation.from}/href`,
path: `/_links/bitstreams${operation.path}/href`
}))])
).subscribe((operations: Operation[]) => this.bundleService.patch(bundle.self, operations));
});
});
} }
/** /**

View File

@@ -104,9 +104,9 @@ export class ServerSyncBufferEffects {
map((entry: ObjectCacheEntry) => { map((entry: ObjectCacheEntry) => {
if (isNotEmpty(entry.patches)) { if (isNotEmpty(entry.patches)) {
const flatPatch: Operation[] = [].concat(...entry.patches.map((patch) => patch.operations)); const flatPatch: Operation[] = [].concat(...entry.patches.map((patch) => patch.operations));
const metadataPatch = flatPatch.filter((op: Operation) => op.path.startsWith('/metadata')); const objectPatch = flatPatch.filter((op: Operation) => op.path.startsWith('/metadata') || op.op === 'move');
if (isNotEmpty(metadataPatch)) { if (isNotEmpty(objectPatch)) {
this.requestService.configure(new PatchRequest(this.requestService.generateRequestId(), href, metadataPatch)); this.requestService.configure(new PatchRequest(this.requestService.generateRequestId(), href, objectPatch));
} }
} }
return new ApplyPatchObjectCacheAction(href); return new ApplyPatchObjectCacheAction(href);

View File

@@ -122,6 +122,7 @@ import { BrowseDefinition } from './shared/browse-definition.model';
import { BitstreamDataService } from './data/bitstream-data.service'; import { BitstreamDataService } from './data/bitstream-data.service';
import { MappedCollectionsReponseParsingService } from './data/mapped-collections-reponse-parsing.service'; import { MappedCollectionsReponseParsingService } from './data/mapped-collections-reponse-parsing.service';
import { ObjectSelectService } from '../shared/object-select/object-select.service'; import { ObjectSelectService } from '../shared/object-select/object-select.service';
import { ArrayMoveChangeAnalyzer } from './data/array-move-change-analyzer.service';
const IMPORTS = [ const IMPORTS = [
CommonModule, CommonModule,
@@ -201,6 +202,7 @@ const PROVIDERS = [
DSpaceObjectDataService, DSpaceObjectDataService,
DSOChangeAnalyzer, DSOChangeAnalyzer,
DefaultChangeAnalyzer, DefaultChangeAnalyzer,
ArrayMoveChangeAnalyzer,
ObjectSelectService, ObjectSelectService,
CSSVariableService, CSSVariableService,
MenuService, MenuService,

View File

@@ -0,0 +1,38 @@
import { MoveOperation, Operation } from 'fast-json-patch/lib/core';
import { compare } from 'fast-json-patch';
import { ChangeAnalyzer } from './change-analyzer';
import { Injectable } from '@angular/core';
import { CacheableObject } from '../cache/object-cache.reducer';
import { NormalizedObject } from '../cache/models/normalized-object.model';
import { moveItemInArray } from '@angular/cdk/drag-drop';
/**
* A class to determine move operations between two arrays
*/
@Injectable()
export class ArrayMoveChangeAnalyzer<T> {
/**
* Compare two arrays detecting and returning move operations
*
* @param array1 The original array
* @param array2 The custom array to compare with the original
*/
diff(array1: T[], array2: T[]): MoveOperation[] {
const result = [];
const moved = [...array1];
array1.forEach((value: T, index: number) => {
const otherIndex = array2.indexOf(value);
const movedIndex = moved.indexOf(value);
if (index !== otherIndex && movedIndex !== otherIndex) {
moveItemInArray(moved, movedIndex, otherIndex);
result.push(Object.assign({
op: 'move',
from: '/' + movedIndex,
path: '/' + otherIndex
}) as MoveOperation)
}
});
return result;
}
}

View File

@@ -24,6 +24,9 @@ import {
import { distinctUntilChanged, filter, map } from 'rxjs/operators'; import { distinctUntilChanged, filter, map } from 'rxjs/operators';
import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util'; import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util';
import { INotification } from '../../../shared/notifications/models/notification.model'; import { INotification } from '../../../shared/notifications/models/notification.model';
import { Operation } from 'fast-json-patch';
import { ArrayMoveChangeAnalyzer } from '../array-move-change-analyzer.service';
import { MoveOperation } from 'fast-json-patch/lib/core';
function objectUpdatesStateSelector(): MemoizedSelector<CoreState, ObjectUpdatesState> { function objectUpdatesStateSelector(): MemoizedSelector<CoreState, ObjectUpdatesState> {
return createSelector(coreSelector, (state: CoreState) => state['cache/object-updates']); return createSelector(coreSelector, (state: CoreState) => state['cache/object-updates']);
@@ -42,7 +45,8 @@ function filterByUrlAndUUIDFieldStateSelector(url: string, uuid: string): Memoiz
*/ */
@Injectable() @Injectable()
export class ObjectUpdatesService { export class ObjectUpdatesService {
constructor(private store: Store<CoreState>) { constructor(private store: Store<CoreState>,
private comparator: ArrayMoveChangeAnalyzer<string>) {
} }
@@ -335,4 +339,16 @@ export class ObjectUpdatesService {
getLastModified(url: string): Observable<Date> { getLastModified(url: string): Observable<Date> {
return this.getObjectEntry(url).pipe(map((entry: ObjectUpdatesEntry) => entry.lastModified)); return this.getObjectEntry(url).pipe(map((entry: ObjectUpdatesEntry) => entry.lastModified));
} }
/**
* Get move operations based on the custom order
* @param url The page's url
*/
getMoveOperations(url: string): Observable<MoveOperation[]> {
return this.getObjectEntry(url).pipe(
map((objectEntry) => objectEntry.customOrder),
map((customOrder) => this.comparator.diff(customOrder.initialOrder, customOrder.newOrder))
);
}
} }