taskid 66076 Keep virtual metadata on item delete - tests & docs & small fixes

This commit is contained in:
Samuel
2019-12-03 16:52:41 +01:00
parent 2eac2a20bb
commit 891415daae
10 changed files with 81 additions and 45 deletions

View File

@@ -1686,6 +1686,6 @@
"uploader.queue-length": "Queue length", "uploader.queue-length": "Queue length",
"virtual-metadata-modal.head": "Select the items for which you want to save the virtual metadata as real metadata", "virtual-metadata.delete-relationship.modal-head": "Select the items for which you want to save the virtual metadata as real metadata",
} }

View File

@@ -99,7 +99,7 @@ export class EditRelationshipComponent implements OnChanges {
).pipe( ).pipe(
map((items: Item[]) => map((items: Item[]) =>
items.map((item) => this.objectUpdatesService items.map((item) => this.objectUpdatesService
.isSelectedVirtualMetadataItem(this.url, this.relationship.id, item.uuid)) .isSelectedVirtualMetadata(this.url, this.relationship.id, item.uuid))
), ),
switchMap((selection$) => observableCombineLatest(selection$)), switchMap((selection$) => observableCombineLatest(selection$)),
map((selection: boolean[]) => { map((selection: boolean[]) => {

View File

@@ -110,11 +110,14 @@ describe('ItemRelationshipsComponent', () => {
relationships[1].rightItem = observableOf(new RemoteData(false, false, true, undefined, item)); relationships[1].rightItem = observableOf(new RemoteData(false, false, true, undefined, item));
fieldUpdate1 = { fieldUpdate1 = {
field: author1, field: relationships[0],
changeType: undefined changeType: undefined
}; };
fieldUpdate2 = { fieldUpdate2 = {
field: author2, field: Object.assign(
relationships[1],
{keepLeftVirtualMetadata: true, keepRightVirtualMetadata: false}
),
changeType: FieldChangeType.REMOVE changeType: FieldChangeType.REMOVE
}; };
@@ -130,12 +133,12 @@ describe('ItemRelationshipsComponent', () => {
objectUpdatesService = jasmine.createSpyObj('objectUpdatesService', objectUpdatesService = jasmine.createSpyObj('objectUpdatesService',
{ {
getFieldUpdates: observableOf({ getFieldUpdates: observableOf({
[author1.uuid]: fieldUpdate1, [relationships[0].uuid]: fieldUpdate1,
[author2.uuid]: fieldUpdate2 [relationships[1].uuid]: fieldUpdate2
}), }),
getFieldUpdatesExclusive: observableOf({ getFieldUpdatesExclusive: observableOf({
[author1.uuid]: fieldUpdate1, [relationships[0].uuid]: fieldUpdate1,
[author2.uuid]: fieldUpdate2 [relationships[1].uuid]: fieldUpdate2
}), }),
saveAddFieldUpdate: {}, saveAddFieldUpdate: {},
discardFieldUpdates: {}, discardFieldUpdates: {},
@@ -227,7 +230,7 @@ describe('ItemRelationshipsComponent', () => {
}); });
it('it should delete the correct relationship', () => { it('it should delete the correct relationship', () => {
expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationships[1].uuid); expect(relationshipService.deleteRelationship).toHaveBeenCalledWith(relationships[1].uuid, 'left');
}); });
}); });
}); });

View File

@@ -116,8 +116,11 @@ export class ItemRelationshipsComponent extends AbstractItemUpdateComponent impl
switchMap((relationships: Relationship[]) => { switchMap((relationships: Relationship[]) => {
return this.objectUpdatesService.getFieldUpdatesExclusive(this.url, relationships) as Observable<FieldUpdates> return this.objectUpdatesService.getFieldUpdatesExclusive(this.url, relationships) as Observable<FieldUpdates>
}), }),
map((fieldUpdates: FieldUpdates) => Object.values(fieldUpdates).filter((fieldUpdate: FieldUpdate) => fieldUpdate.changeType === FieldChangeType.REMOVE)), map((fieldUpdates: FieldUpdates) =>
map((fieldUpdates: FieldUpdate[]) => fieldUpdates.map((fieldUpdate: FieldUpdate) => fieldUpdate.field) as DeleteRelationship[]), Object.values(fieldUpdates)
.filter((fieldUpdate: FieldUpdate) => fieldUpdate.changeType === FieldChangeType.REMOVE)
.map((fieldUpdate: FieldUpdate) => fieldUpdate.field as DeleteRelationship)
),
isNotEmptyOperator(), isNotEmptyOperator(),
); );
removedRelationshipIDs$.pipe( removedRelationshipIDs$.pipe(

View File

@@ -1,5 +1,5 @@
<div> <div>
<div class="modal-header">{{'virtual-metadata-modal.head' | translate}} <div class="modal-header">{{'virtual-metadata.delete-relationship.modal-head' | translate}}
<button type="button" class="close" (click)="close.emit()" aria-label="Close"> <button type="button" class="close" (click)="close.emit()" aria-label="Close">
<span aria-hidden="true">×</span> <span aria-hidden="true">×</span>
</button> </button>

View File

@@ -48,15 +48,27 @@ export class VirtualMetadataComponent implements OnChanges {
getVirtualMetadata(relationship: Relationship, relatedItem: Item): VirtualMetadata[] { getVirtualMetadata(relationship: Relationship, relatedItem: Item): VirtualMetadata[] {
return this.objectUpdatesService.getVirtualMetadataList(relationship, relatedItem); return Object.entries(relatedItem.metadata)
.map(([key, value]) =>
value
.filter((metadata: MetadataValue) =>
metadata.authority && metadata.authority.endsWith(relationship.id))
.map((metadata: MetadataValue) => {
return {
metadataField: key,
metadataValue: metadata,
}
})
)
.reduce((previous, current) => previous.concat(current));
} }
setSelectedVirtualMetadataItem(item: Item, selected: boolean) { setSelectedVirtualMetadataItem(item: Item, selected: boolean) {
this.objectUpdatesService.setSelectedVirtualMetadataItem(this.url, this.relationship.id, item.uuid, selected); this.objectUpdatesService.setSelectedVirtualMetadata(this.url, this.relationship.id, item.uuid, selected);
} }
isSelectedVirtualMetadataItem(item: Item): Observable<boolean> { isSelectedVirtualMetadataItem(item: Item): Observable<boolean> {
return this.objectUpdatesService.isSelectedVirtualMetadataItem(this.url, this.relationship.id, item.uuid); return this.objectUpdatesService.isSelectedVirtualMetadata(this.url, this.relationship.id, item.uuid);
} }
} }

View File

@@ -5,10 +5,11 @@ import {
FieldChangeType, FieldChangeType,
InitializeFieldsAction, InitializeFieldsAction,
ReinstateObjectUpdatesAction, ReinstateObjectUpdatesAction,
RemoveFieldUpdateAction, RemoveObjectUpdatesAction, RemoveFieldUpdateAction, RemoveObjectUpdatesAction, SelectVirtualMetadataAction,
SetEditableFieldUpdateAction, SetValidFieldUpdateAction SetEditableFieldUpdateAction, SetValidFieldUpdateAction
} from './object-updates.actions'; } from './object-updates.actions';
import { OBJECT_UPDATES_TRASH_PATH, objectUpdatesReducer } from './object-updates.reducer'; import { OBJECT_UPDATES_TRASH_PATH, objectUpdatesReducer } from './object-updates.reducer';
import {Relationship} from "../../shared/item-relationships/relationship.model";
class NullAction extends RemoveFieldUpdateAction { class NullAction extends RemoveFieldUpdateAction {
type = null; type = null;
@@ -44,6 +45,7 @@ const identifiable3 = {
language: null, language: null,
value: 'Unchanged value' value: 'Unchanged value'
}; };
const relationship: Relationship = Object.assign(new Relationship, {uuid: 'test relationship uuid'});
const modDate = new Date(2010, 2, 11); const modDate = new Date(2010, 2, 11);
const uuid = identifiable1.uuid; const uuid = identifiable1.uuid;
@@ -80,7 +82,9 @@ describe('objectUpdatesReducer', () => {
} }
}, },
lastModified: modDate, lastModified: modDate,
virtualMetadataSources: {}, virtualMetadataSources: {
[relationship.uuid]: {[identifiable1.uuid]: true}
},
} }
}; };
@@ -103,7 +107,10 @@ describe('objectUpdatesReducer', () => {
isValid: true isValid: true
}, },
}, },
lastModified: modDate lastModified: modDate,
virtualMetadataSources: {
[relationship.uuid]: {[identifiable1.uuid]: true}
},
}, },
[url + OBJECT_UPDATES_TRASH_PATH]: { [url + OBJECT_UPDATES_TRASH_PATH]: {
fieldStates: { fieldStates: {
@@ -134,7 +141,10 @@ describe('objectUpdatesReducer', () => {
changeType: FieldChangeType.ADD changeType: FieldChangeType.ADD
} }
}, },
lastModified: modDate lastModified: modDate,
virtualMetadataSources: {
[relationship.uuid]: {[identifiable1.uuid]: true}
},
} }
}; };
@@ -196,6 +206,12 @@ describe('objectUpdatesReducer', () => {
objectUpdatesReducer(testState, action); objectUpdatesReducer(testState, action);
}); });
it('should perform the SELECT_VIRTUAL_METADATA action without affecting the previous state', () => {
const action = new SelectVirtualMetadataAction(url, relationship.uuid, identifiable1.uuid, true);
// testState has already been frozen above
objectUpdatesReducer(testState, action);
});
it('should initialize all fields when the INITIALIZE action is dispatched, based on the payload', () => { it('should initialize all fields when the INITIALIZE action is dispatched, based on the payload', () => {
const action = new InitializeFieldsAction(url, [identifiable1, identifiable3], modDate); const action = new InitializeFieldsAction(url, [identifiable1, identifiable3], modDate);

View File

@@ -58,14 +58,24 @@ export interface FieldUpdates {
[uuid: string]: FieldUpdate; [uuid: string]: FieldUpdate;
} }
/**
* The states of all virtual metadata selections available for a single page, mapped by the relationship uuid
*/
export interface VirtualMetadataSources { export interface VirtualMetadataSources {
[source: string]: VirtualMetadataSource [source: string]: VirtualMetadataSource
} }
/**
* The selection of virtual metadata for a relationship, mapped by the uuid of either the item or the relationship type
*/
export interface VirtualMetadataSource { export interface VirtualMetadataSource {
[uuid: string]: boolean, [uuid: string]: boolean,
} }
/**
* A fieldupdate interface which represents a relationship selected to be deleted,
* along with a selection of the virtual metadata to keep
*/
export interface DeleteRelationship extends Relationship { export interface DeleteRelationship extends Relationship {
keepLeftVirtualMetadata: boolean, keepLeftVirtualMetadata: boolean,
keepRightVirtualMetadata: boolean, keepRightVirtualMetadata: boolean,
@@ -192,7 +202,7 @@ function addFieldUpdate(state: any, action: AddFieldUpdateAction) {
} }
/** /**
* Add a new update for a specific field to the store * Update the selected virtual metadata in the store
* @param state The current state * @param state The current state
* @param action The action to perform on the current state * @param action The action to perform on the current state
*/ */

View File

@@ -4,13 +4,14 @@ import { ObjectUpdatesService } from './object-updates.service';
import { import {
DiscardObjectUpdatesAction, DiscardObjectUpdatesAction,
FieldChangeType, FieldChangeType,
InitializeFieldsAction, ReinstateObjectUpdatesAction, RemoveFieldUpdateAction, InitializeFieldsAction, ReinstateObjectUpdatesAction, RemoveFieldUpdateAction, SelectVirtualMetadataAction,
SetEditableFieldUpdateAction SetEditableFieldUpdateAction
} from './object-updates.actions'; } from './object-updates.actions';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { Notification } from '../../../shared/notifications/models/notification.model'; import { Notification } from '../../../shared/notifications/models/notification.model';
import { NotificationType } from '../../../shared/notifications/models/notification-type'; import { NotificationType } from '../../../shared/notifications/models/notification-type';
import { OBJECT_UPDATES_TRASH_PATH } from './object-updates.reducer'; import { OBJECT_UPDATES_TRASH_PATH } from './object-updates.reducer';
import {Relationship} from "../../shared/item-relationships/relationship.model";
describe('ObjectUpdatesService', () => { describe('ObjectUpdatesService', () => {
let service: ObjectUpdatesService; let service: ObjectUpdatesService;
@@ -22,6 +23,7 @@ describe('ObjectUpdatesService', () => {
const identifiable2 = { uuid: '26cbb5ce-5786-4e57-a394-b9fcf8eaf241' }; const identifiable2 = { uuid: '26cbb5ce-5786-4e57-a394-b9fcf8eaf241' };
const identifiable3 = { uuid: 'c5d2c2f7-d757-48bf-84cc-8c9229c8407e' }; const identifiable3 = { uuid: 'c5d2c2f7-d757-48bf-84cc-8c9229c8407e' };
const identifiables = [identifiable1, identifiable2]; const identifiables = [identifiable1, identifiable2];
const relationship: Relationship = Object.assign(new Relationship, {uuid: 'test relationship uuid'});
const fieldUpdates = { const fieldUpdates = {
[identifiable1.uuid]: { field: identifiable1Updated, changeType: FieldChangeType.UPDATE }, [identifiable1.uuid]: { field: identifiable1Updated, changeType: FieldChangeType.UPDATE },
@@ -38,7 +40,7 @@ describe('ObjectUpdatesService', () => {
}; };
const objectEntry = { const objectEntry = {
fieldStates, fieldUpdates, lastModified: modDate fieldStates, fieldUpdates, lastModified: modDate, virtualMetadataSources: {}
}; };
store = new Store<CoreState>(undefined, undefined, undefined); store = new Store<CoreState>(undefined, undefined, undefined);
spyOn(store, 'dispatch'); spyOn(store, 'dispatch');
@@ -251,4 +253,10 @@ describe('ObjectUpdatesService', () => {
}); });
}); });
describe('setSelectedVirtualMetadata', () => {
it('should dispatch a SELECT_VIRTUAL_METADATA action with the correct URL, relationship, identifiable and boolean', () => {
service.setSelectedVirtualMetadata(url, relationship.uuid, identifiable1.uuid, true);
expect(store.dispatch).toHaveBeenCalledWith(new SelectVirtualMetadataAction(url, relationship.uuid, identifiable1.uuid, true));
});
});
}); });

View File

@@ -204,24 +204,7 @@ export class ObjectUpdatesService {
saveChangeFieldUpdate(url: string, field: Identifiable) { saveChangeFieldUpdate(url: string, field: Identifiable) {
this.saveFieldUpdate(url, field, FieldChangeType.UPDATE); this.saveFieldUpdate(url, field, FieldChangeType.UPDATE);
} }
isSelectedVirtualMetadata(url: string, relationship: string, item: string): Observable<boolean> {
getVirtualMetadataList(relationship: Relationship, item: Item): VirtualMetadata[] {
return Object.entries(item.metadata)
.map(([key, value]) =>
value
.filter((metadata: MetadataValue) =>
metadata.authority && metadata.authority.endsWith(relationship.id))
.map((metadata: MetadataValue) => {
return {
metadataField: key,
metadataValue: metadata,
}
})
)
.reduce((previous, current) => previous.concat(current));
}
isSelectedVirtualMetadataItem(url: string, relationship: string, item: string): Observable<boolean> {
return this.store return this.store
.pipe( .pipe(
@@ -231,13 +214,14 @@ export class ObjectUpdatesService {
} }
/** /**
* Method to dispatch an AddFieldUpdateAction to the store * Method to dispatch a SelectVirtualMetadataAction to the store
* @param url The page's URL for which the changes are saved * @param url The page's URL for which the changes are saved
* @param field An updated field for the page's object * @param relationship the relationship for which virtual metadata is selected
* @param changeType The last type of change applied to this field * @param uuid the selection identifier, can either be the item uuid or the relationship type uuid
* @param selected whether or not to select the virtual metadata to be saved
*/ */
setSelectedVirtualMetadataItem(url: string, relationship: string, item: string, selected: boolean) { setSelectedVirtualMetadata(url: string, relationship: string, uuid: string, selected: boolean) {
this.store.dispatch(new SelectVirtualMetadataAction(url, relationship, item, selected)); this.store.dispatch(new SelectVirtualMetadataAction(url, relationship, uuid, selected));
} }
/** /**