Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/app/core/submission/workflowitem-data.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
import { ObjectCacheService } from '../cache/object-cache.service';
import { RestResponse } from '../cache/response.models';
import { CoreState } from '../core-state.model';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { HrefOnlyDataService } from '../data/href-only-data.service';
import { RequestService } from '../data/request.service';
import { RequestEntry } from '../data/request-entry.model';
Expand Down Expand Up @@ -91,6 +92,7 @@ describe('WorkflowItemDataService test', () => {
objectCache,
halService,
notificationsService,
new DefaultChangeAnalyzer(),
);
}

Expand Down
38 changes: 37 additions & 1 deletion src/app/core/submission/workflowitem-data.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Injectable } from '@angular/core';
import { RestRequestMethod } from '@dspace/config/rest-request-method';
import { hasValue } from '@dspace/shared/utils/empty.util';
import { Operation } from 'fast-json-patch';
import { Observable } from 'rxjs';
import {
find,
Expand All @@ -14,10 +16,15 @@ import {
DeleteDataImpl,
} from '../data/base/delete-data';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import {
PatchData,
PatchDataImpl,
} from '../data/base/patch-data';
import {
SearchData,
SearchDataImpl,
} from '../data/base/search-data';
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
import { FindListOptions } from '../data/find-list-options.model';
import { PaginatedList } from '../data/paginated-list.model';
import { RemoteData } from '../data/remote-data';
Expand All @@ -30,28 +37,41 @@ import { NoContent } from '../shared/NoContent.model';
import { getFirstCompletedRemoteData } from '../shared/operators';
import { WorkflowItem } from './models/workflowitem.model';

/**
* Constructs an endpoint taking into account that the WorkflowItem's "uuid" has a prefix by removing that prefix from the ID
* @param endpoint Endpoint to append ID to
* @param resourceID WorkflowItem's "uuid" including the prefix to remove
*/
const constructWorkflowItemIdEndpoint = (endpoint, resourceID) => {
const regex = new RegExp(`^${WorkflowItem.type.value}-`);
return `${endpoint}/${resourceID.replace(regex, '')}`;
};

/**
* A service that provides methods to make REST requests with workflow items endpoint.
*/
@Injectable({ providedIn: 'root' })
export class WorkflowItemDataService extends IdentifiableDataService<WorkflowItem> implements SearchData<WorkflowItem>, DeleteData<WorkflowItem> {
export class WorkflowItemDataService extends IdentifiableDataService<WorkflowItem> implements SearchData<WorkflowItem>, DeleteData<WorkflowItem>, PatchData<WorkflowItem> {
protected searchByItemLinkPath = 'item';
protected responseMsToLive = 10 * 1000;

private searchData: SearchDataImpl<WorkflowItem>;
private deleteData: DeleteDataImpl<WorkflowItem>;
private patchData: PatchDataImpl<WorkflowItem>;

constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected halService: HALEndpointService,
protected notificationsService: NotificationsService,
protected comparator: DefaultChangeAnalyzer<WorkflowItem>,
) {
super('workflowitems', requestService, rdbService, objectCache, halService);

this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
this.patchData = new PatchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.comparator, this.responseMsToLive, constructWorkflowItemIdEndpoint);
}

/**
Expand Down Expand Up @@ -148,4 +168,20 @@ export class WorkflowItemDataService extends IdentifiableDataService<WorkflowIte
public deleteByHref(href: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.deleteByHref(href, copyVirtualMetadata);
}

commitUpdates(method?: RestRequestMethod): void {
this.patchData.commitUpdates(method);
}

createPatchFromCache(object: WorkflowItem): Observable<Operation[]> {
return this.patchData.createPatchFromCache(object);
}

patch(object: WorkflowItem, operations: Operation[]): Observable<RemoteData<WorkflowItem>> {
return this.patchData.patch(object, operations);
}

update(object: WorkflowItem): Observable<RemoteData<WorkflowItem>> {
return this.patchData.update(object);
}
}
40 changes: 38 additions & 2 deletions src/app/core/submission/workspaceitem-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import {
HttpHeaders,
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { RestRequestMethod } from '@dspace/config/rest-request-method';
import { hasValue } from '@dspace/shared/utils/empty.util';
import { Store } from '@ngrx/store';
import { Operation } from 'fast-json-patch';
import { Observable } from 'rxjs';
import {
find,
Expand All @@ -20,6 +22,10 @@ import {
DeleteDataImpl,
} from '../data/base/delete-data';
import { IdentifiableDataService } from '../data/base/identifiable-data.service';
import {
PatchData,
PatchDataImpl,
} from '../data/base/patch-data';
import {
SearchData,
SearchDataImpl,
Expand All @@ -37,15 +43,26 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
import { NoContent } from '../shared/NoContent.model';
import { WorkspaceItem } from './models/workspaceitem.model';

/**
* Constructs an endpoint taking into account that the WorkspaceItem's "uuid" has a prefix by removing that prefix from the ID
* @param endpoint Endpoint to append ID to
* @param resourceID WorkspaceItem's "uuid" including the prefix to remove
*/
const constructWorkspaceItemIdEndpoint = (endpoint, resourceID) => {
const regex = new RegExp(`^${WorkspaceItem.type.value}-`);
return `${endpoint}/${resourceID.replace(regex, '')}`;
};

/**
* A service that provides methods to make REST requests with workspaceitems endpoint.
*/
@Injectable({ providedIn: 'root' })
export class WorkspaceitemDataService extends IdentifiableDataService<WorkspaceItem> implements DeleteData<WorkspaceItem>, SearchData<WorkspaceItem>{
export class WorkspaceitemDataService extends IdentifiableDataService<WorkspaceItem> implements DeleteData<WorkspaceItem>, SearchData<WorkspaceItem>, PatchData<WorkspaceItem> {
protected linkPath = 'workspaceitems';
protected searchByItemLinkPath = 'item';
private deleteData: DeleteData<WorkspaceItem>;
private searchData: SearchDataImpl<WorkspaceItem>;
private patchData: PatchDataImpl<WorkspaceItem>;

constructor(
protected comparator: DSOChangeAnalyzer<WorkspaceItem>,
Expand All @@ -55,10 +72,12 @@ export class WorkspaceitemDataService extends IdentifiableDataService<WorkspaceI
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected objectCache: ObjectCacheService,
protected store: Store<CoreState>) {
protected store: Store<CoreState>,
) {
super('workspaceitems', requestService, rdbService, objectCache, halService);
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
this.searchData = new SearchDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, this.responseMsToLive);
this.patchData = new PatchDataImpl<WorkspaceItem>(this.linkPath, requestService, rdbService, objectCache, halService, this.comparator, this.responseMsToLive, constructWorkspaceItemIdEndpoint);
}
public delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
return this.deleteData.delete(objectId, copyVirtualMetadata);
Expand Down Expand Up @@ -137,4 +156,21 @@ export class WorkspaceitemDataService extends IdentifiableDataService<WorkspaceI
searchBy(searchMethod: string, options?: FindListOptions, useCachedVersionIfAvailable?: boolean, reRequestOnStale?: boolean, ...linksToFollow: FollowLinkConfig<WorkspaceItem>[]): Observable<RemoteData<PaginatedList<WorkspaceItem>>> {
return this.searchData.searchBy(searchMethod, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}

commitUpdates(method?: RestRequestMethod): void {
this.patchData.commitUpdates(method);
}

createPatchFromCache(object: WorkspaceItem): Observable<Operation[]> {
return this.patchData.createPatchFromCache(object);
}

patch(object: WorkspaceItem, operations: Operation[]): Observable<RemoteData<WorkspaceItem>> {
return this.patchData.patch(object, operations);
}

update(object: WorkspaceItem): Observable<RemoteData<WorkspaceItem>> {
return this.patchData.update(object);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ describe('DsDynamicFormControlContainerComponent test suite', () => {
{ provide: DYNAMIC_FORM_CONTROL_MAP_FN, useValue: dsDynamicFormControlMapFn },
{ provide: LiveRegionService, useValue: getLiveRegionServiceStub() },
{ provide: Actions, useValue: actions$ },
{ provide: 'sectionDataProvider', useValue: { id: 'mock-section-id' } },
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents().then(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ import {

import { AppState } from '../../../../app.reducer';
import { SubmissionObjectActionTypes } from '../../../../submission/objects/submission-objects.actions';
import { SectionDataObject } from '../../../../submission/sections/models/section-data.model';
import { SubmissionService } from '../../../../submission/submission.service';
import { SubmissionObjectService } from '../../../../submission/submission-object.service';
import { LiveRegionService } from '../../../live-region/live-region.service';
Expand Down Expand Up @@ -165,6 +166,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
@Input() hasErrorMessaging = false;
@Input() layout = null as DynamicFormLayout;
@Input() model: any;
@Input() arrayIndex: number;
relationshipValue$: Observable<ReorderableRelationship>;
isRelationship: boolean;
modalRef: NgbModalRef;
Expand Down Expand Up @@ -223,6 +225,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
@Inject(APP_CONFIG) protected appConfig: AppConfig,
@Inject(DYNAMIC_FORM_CONTROL_MAP_FN) protected dynamicFormControlFn: DynamicFormControlMapFn,
private actions$: Actions,
@Inject('sectionDataProvider') public sectionData: SectionDataObject,
) {
super(ref, componentFactoryResolver, layoutService, validationService, dynamicFormComponentService, relationService);
this.fetchThumbnail = this.appConfig.browseBy.showThumbnails;
Expand Down Expand Up @@ -459,9 +462,10 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
} else if (typeof this.model.value.value === 'string') {
modalComp.query = this.model.value.value;
// If the existing value is not virtual, store properties on the modal required to perform a replace operation
if (!this.model.value.isVirtual) {
modalComp.replaceValuePlace = this.model.value.place;
if (!this.model.value.isVirtual && hasValue(this.arrayIndex)) {
modalComp.replaceValuePlace = this.arrayIndex;
modalComp.replaceValueMetadataField = this.model.name;
modalComp.replaceValueSection = this.sectionData?.id;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
[class.d-none]="_model.hidden"
[layout]="formLayout"
[model]="_model"
[arrayIndex]="idx"
[templates]="templates"
[ngClass]="[getClass('element', 'host', _model), getClass('grid', 'host', _model)]"
(dfBlur)="onBlur($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ describe('DsDynamicFormArrayComponent', () => {
{ provide: APP_CONFIG, useValue: environment },
{ provide: DYNAMIC_FORM_CONTROL_MAP_FN, useValue: dsDynamicFormControlMapFn },
{ provide: LiveRegionService, useValue: getLiveRegionServiceStub() },
{ provide: 'sectionDataProvider', useValue: { id: 'mock-section-id' } },
],
}).overrideComponent(DsDynamicFormArrayComponent, {
remove: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ describe('DsDynamicRelationGroupComponent test suite', () => {
{ provide: APP_DATA_SERVICES_MAP, useValue: {} },
{ provide: DYNAMIC_FORM_CONTROL_MAP_FN, useValue: dsDynamicFormControlMapFn },
{ provide: LiveRegionService, useValue: getLiveRegionServiceStub() },
{ provide: 'sectionDataProvider', useValue: { id: 'mock-section-id' } },
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,12 @@ describe('DsDynamicLookupRelationModalComponent', () => {
beforeEach(() => {
component.replaceValuePlace = 3;
component.replaceValueMetadataField = 'dc.subject';
component.replaceValueSection = 'traditionalpageone';
});

it('should dispatch a ReplaceRelationshipAction for the first selected object and a AddRelationshipAction for every other selected object', () => {
component.select(searchResult1, searchResult2, searchResult3);
const action1 = new ReplaceRelationshipAction(component.item, searchResult1.indexableObject, true, 3, 'dc.subject', relationship.relationshipType, submissionId, nameVariant);
const action1 = new ReplaceRelationshipAction(component.item, searchResult1.indexableObject, true, 3, 'dc.subject', 'traditionalpageone', relationship.relationshipType, submissionId, nameVariant);
const action2 = new AddRelationshipAction(component.item, searchResult2.indexableObject, relationship.relationshipType, submissionId, nameVariant);
const action3 = new AddRelationshipAction(component.item, searchResult3.indexableObject, relationship.relationshipType, submissionId, nameVariant);

Expand All @@ -219,6 +220,7 @@ describe('DsDynamicLookupRelationModalComponent', () => {
expect((component as any).store.dispatch).toHaveBeenCalledWith(action3);
expect(component.replaceValuePlace).toBeUndefined();
expect(component.replaceValueMetadataField).toBeUndefined();
expect(component.replaceValueSection).toBeUndefined();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
*/
replaceValueMetadataField: string;

/**
* The submission section of the plain-text value that should be replaced by adding a relationship
*/
replaceValueSection: string;

/**
* A map of subscriptions within this component
*/
Expand Down Expand Up @@ -326,7 +331,7 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
let action;
if (i === 0 && hasValue(this.replaceValueMetadataField)) {
// This is the first action this modal performs and "replace" properties are present to replace an existing metadata value
action = new ReplaceRelationshipAction(this.item, object.item, true, this.replaceValuePlace, this.replaceValueMetadataField, this.relationshipOptions.relationshipType, this.submissionId, object.nameVariant);
action = new ReplaceRelationshipAction(this.item, object.item, true, this.replaceValuePlace, this.replaceValueMetadataField, this.replaceValueSection, this.relationshipOptions.relationshipType, this.submissionId, object.nameVariant);
// Only "replace" once, reset replace properties so future actions become "add"
this.resetReplaceProperties();
} else {
Expand Down Expand Up @@ -396,6 +401,7 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy
private resetReplaceProperties() {
this.replaceValueMetadataField = undefined;
this.replaceValuePlace = undefined;
this.replaceValueSection = undefined;
}

ngOnDestroy() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export class ReplaceRelationshipAction implements Action {
replaceLeftSide: boolean;
place: number;
mdField: string;
section: string;
relationshipType: string;
submissionId: string;
nameVariant: string;
Expand All @@ -158,6 +159,7 @@ export class ReplaceRelationshipAction implements Action {
* @param replaceLeftSide If true, the item on the left side (item1) will have its metadata value replaced
* @param place The index of the metadata value that should be replaced with the new relationship
* @param mdField The metadata field of the value to replace
* @param section The submission section of the value to replace
* @param relationshipType The label of the relationshipType
* @param submissionId The current submissionId
* @param nameVariant The nameVariant of the relationshipType
Expand All @@ -168,11 +170,12 @@ export class ReplaceRelationshipAction implements Action {
replaceLeftSide: boolean,
place: number,
mdField: string,
section: string,
relationshipType: string,
submissionId: string,
nameVariant?: string,
) {
this.payload = { item1, item2, replaceLeftSide, place, mdField, relationshipType, submissionId, nameVariant };
this.payload = { item1, item2, replaceLeftSide, place, mdField, section, relationshipType, submissionId, nameVariant };
}
}

Expand Down
Loading
Loading