64961: Edit bitstream metadata PATCH requests

This commit is contained in:
Kristof De Langhe
2019-09-13 14:39:40 +02:00
parent 91c6b76230
commit 1d3ada20f9
7 changed files with 78 additions and 17 deletions

View File

@@ -91,6 +91,8 @@
"bitstream.edit.form.fileName.label": "Filename",
"bitstream.edit.form.fileName.hint": "Change the filename for the bitstream. Note that this will change the display bitstream URL, but old links will still resolve as long as the sequence ID does not change.",
"bitstream.edit.form.primaryBitstream.label": "Primary bitstream",
"bitstream.edit.notifications.saved.content": "Your changes to this bitstream were saved.",
"bitstream.edit.notifications.saved.title": "Bitstream saved",
"browse.comcol.by.author": "By Author",
"browse.comcol.by.dateissued": "By Issue Date",
"browse.comcol.by.subject": "By Subject",

View File

@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
import { Bitstream } from '../../core/shared/bitstream.model';
import { ActivatedRoute } from '@angular/router';
import { map } from 'rxjs/operators';
import { map, tap } from 'rxjs/operators';
import { Subscription } from 'rxjs/internal/Subscription';
import {
DynamicFormControlModel, DynamicFormGroupModel, DynamicFormLayout, DynamicFormService,
@@ -11,6 +11,11 @@ import {
import { FormGroup } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { DynamicCustomSwitchModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.model';
import { cloneDeep } from 'lodash';
import { BitstreamDataService } from '../../core/data/bitstream-data.service';
import { getSucceededRemoteData } from '../../core/shared/operators';
import { RemoteData } from '../../core/data/remote-data';
import { NotificationsService } from '../../shared/notifications/notifications.service';
@Component({
selector: 'ds-edit-bitstream-page',
@@ -43,6 +48,11 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
*/
HINT_KEY_SUFFIX = '.hint';
/**
* @type {string} Key prefix used to generate notification messages
*/
NOTIFICATIONS_PREFIX = 'bitstream.edit.notifications.';
/**
* The Dynamic Input Model for the file's name
*/
@@ -167,7 +177,9 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
constructor(private route: ActivatedRoute,
private formService: DynamicFormService,
private translate: TranslateService) {
private translate: TranslateService,
private bitstreamService: BitstreamDataService,
private notificationsService: NotificationsService) {
}
/**
@@ -232,8 +244,32 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
* Check for changes against the bitstream and send update requests to the REST API
*/
onSubmit() {
// TODO: Check for changes against the bitstream and send requests to the REST API accordingly
console.log(this.formGroup.getRawValue());
const updatedValues = this.formGroup.getRawValue();
const newBitstream = this.formToBitstream(updatedValues);
this.bitstreamService.update(newBitstream).pipe(
tap(() => this.bitstreamService.commitUpdates()),
getSucceededRemoteData()
).subscribe((bitstreamRD: RemoteData<Bitstream>) => {
this.bitstream = bitstreamRD.payload;
this.updateForm(this.bitstream);
this.notificationsService.success(
this.translate.instant(this.NOTIFICATIONS_PREFIX + 'saved.title'),
this.translate.instant(this.NOTIFICATIONS_PREFIX + 'saved.content')
);
});
}
/**
* Parse form data to an updated bitstream object
* @param rawForm Raw form data
*/
formToBitstream(rawForm): Bitstream {
const newBitstream = cloneDeep(this.bitstream);
// TODO: Set bitstream to primary when supported
const primary = rawForm.fileNamePrimaryContainer.primaryBitstream;
newBitstream.name = rawForm.fileNamePrimaryContainer.fileName;
newBitstream.description = rawForm.descriptionContainer.description;
return newBitstream;
}
/**

View File

@@ -15,7 +15,7 @@ import { Action, createSelector, MemoizedSelector, select, Store } from '@ngrx/s
import { ServerSyncBufferEntry, ServerSyncBufferState } from './server-sync-buffer.reducer';
import { combineLatest as observableCombineLatest, of as observableOf } from 'rxjs';
import { RequestService } from '../data/request.service';
import { PutRequest } from '../data/request.models';
import { PatchRequest, PutRequest } from '../data/request.models';
import { ObjectCacheService } from './object-cache.service';
import { ApplyPatchObjectCacheAction } from './object-cache.actions';
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
@@ -23,6 +23,8 @@ import { GenericConstructor } from '../shared/generic-constructor';
import { hasValue, isNotEmpty, isNotUndefined } from '../../shared/empty.util';
import { Observable } from 'rxjs/internal/Observable';
import { RestRequestMethod } from '../data/rest-request-method';
import { ObjectCacheEntry } from './object-cache.reducer';
import { Operation } from 'fast-json-patch';
@Injectable()
export class ServerSyncBufferEffects {
@@ -96,15 +98,16 @@ export class ServerSyncBufferEffects {
* @returns {Observable<Action>} ApplyPatchObjectCacheAction to be dispatched
*/
private applyPatch(href: string): Observable<Action> {
const patchObject = this.objectCache.getObjectBySelfLink(href).pipe(take(1));
const patchObject = this.objectCache.getBySelfLink(href).pipe(take(1));
return patchObject.pipe(
map((object) => {
const serializedObject = new DSpaceRESTv2Serializer(object.constructor as GenericConstructor<{}>).serialize(object);
this.requestService.configure(new PutRequest(this.requestService.generateRequestId(), href, serializedObject));
return new ApplyPatchObjectCacheAction(href)
map((entry: ObjectCacheEntry) => {
if (isNotEmpty(entry.patches)) {
const flatPatch: Operation[] = [].concat(...entry.patches.map((patch) => patch.operations));
this.requestService.configure(new PatchRequest(this.requestService.generateRequestId(), href, flatPatch));
return new ApplyPatchObjectCacheAction(href);
}
// this.requestService.configure(new PutRequest(this.requestService.generateRequestId(), href, serializedObject));
})
)
}

View File

@@ -22,6 +22,9 @@ export class DSOChangeAnalyzer<T extends DSpaceObject> implements ChangeAnalyzer
* The second object to compare
*/
diff(object1: T | NormalizedDSpaceObject<T>, object2: T | NormalizedDSpaceObject<T>): Operation[] {
return compare(object1.metadata, object2.metadata).map((operation: Operation) => Object.assign({}, operation, { path: '/metadata' + operation.path }));
return compare(object1.metadata, object2.metadata)
// Filter out operations on UUIDs, as they should never change
.filter((operation: Operation) => !operation.path.endsWith('/uuid'))
.map((operation: Operation) => Object.assign({}, operation, { path: '/metadata' + operation.path }));
}
}

View File

@@ -94,7 +94,6 @@ export class ObjectUpdatesService {
const objectUpdates = this.getObjectEntry(url);
return objectUpdates.pipe(map((objectEntry) => {
const fieldUpdates: FieldUpdates = {};
console.log(objectEntry);
Object.keys(ignoreStates ? objectEntry.fieldUpdates : objectEntry.fieldStates).forEach((uuid) => {
let fieldUpdate = objectEntry.fieldUpdates[uuid];
if (isEmpty(fieldUpdate)) {

View File

@@ -4,19 +4,34 @@ import { Item } from './item.model';
import { BitstreamFormat } from './bitstream-format.model';
import { Observable } from 'rxjs';
import { ResourceType } from './resource-type';
import { hasValue, isUndefined } from '../../shared/empty.util';
export class Bitstream extends DSpaceObject {
static type = new ResourceType('bitstream');
private _description: string;
/**
* The size of this bitstream in bytes
*/
sizeBytes: number;
/**
* The description of this Bitstream
* Get the description of this Bitstream
*/
description: string;
get description(): string {
return (isUndefined(this._description)) ? this.firstMetadataValue('dc.description') : this._description;
}
/**
* Set the description of this Bitstream
*/
set description(description) {
if (hasValue(this.firstMetadata('dc.description'))) {
this.firstMetadata('dc.description').value = description;
}
this._description = description;
}
/**
* The name of the Bundle this Bitstream is part of

View File

@@ -7,7 +7,7 @@ import {
MetadatumViewModel
} from './metadata.models';
import { Metadata } from './metadata.utils';
import { hasNoValue, isUndefined } from '../../shared/empty.util';
import { hasNoValue, hasValue, isUndefined } from '../../shared/empty.util';
import { CacheableObject } from '../cache/object-cache.reducer';
import { RemoteData } from '../data/remote-data';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
@@ -47,6 +47,9 @@ export class DSpaceObject implements CacheableObject, ListableObject {
* The name for this DSpaceObject
*/
set name(name) {
if (hasValue(this.firstMetadata('dc.title'))) {
this.firstMetadata('dc.title').value = name;
}
this._name = name;
}