mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-11 12:03:03 +00:00
[TLC-674] Duplicate detection frontend changes as per feedback
This commit is contained in:
@@ -198,7 +198,7 @@ import { NotifyRequestsStatus } from '../item-page/simple/notify-requests-status
|
|||||||
import { LdnService } from '../admin/admin-ldn-services/ldn-services-model/ldn-services.model';
|
import { LdnService } from '../admin/admin-ldn-services/ldn-services-model/ldn-services.model';
|
||||||
import { Itemfilter } from '../admin/admin-ldn-services/ldn-services-model/ldn-service-itemfilters';
|
import { Itemfilter } from '../admin/admin-ldn-services/ldn-services-model/ldn-service-itemfilters';
|
||||||
import { SubmissionCoarNotifyConfig } from '../submission/sections/section-coar-notify/submission-coar-notify.config';
|
import { SubmissionCoarNotifyConfig } from '../submission/sections/section-coar-notify/submission-coar-notify.config';
|
||||||
import { DuplicateDataService } from './data/duplicate-search.service';
|
import { SubmissionDuplicateDataService } from './submission/submission-duplicate-data.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When not in production, endpoint responses can be mocked for testing purposes
|
* When not in production, endpoint responses can be mocked for testing purposes
|
||||||
@@ -235,7 +235,7 @@ const PROVIDERS = [
|
|||||||
HALEndpointService,
|
HALEndpointService,
|
||||||
HostWindowService,
|
HostWindowService,
|
||||||
ItemDataService,
|
ItemDataService,
|
||||||
DuplicateDataService,
|
SubmissionDuplicateDataService,
|
||||||
MetadataService,
|
MetadataService,
|
||||||
ObjectCacheService,
|
ObjectCacheService,
|
||||||
PaginationComponentOptions,
|
PaginationComponentOptions,
|
||||||
|
@@ -46,9 +46,6 @@ import { RestRequestMethod } from './rest-request-method';
|
|||||||
import { CreateData, CreateDataImpl } from './base/create-data';
|
import { CreateData, CreateDataImpl } from './base/create-data';
|
||||||
import { RequestParam } from '../cache/models/request-param.model';
|
import { RequestParam } from '../cache/models/request-param.model';
|
||||||
import { dataService } from './base/data-service.decorator';
|
import { dataService } from './base/data-service.decorator';
|
||||||
import { Duplicate } from '../../shared/object-list/duplicate-data/duplicate.model';
|
|
||||||
import { SearchDataImpl } from './base/search-data';
|
|
||||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract service for CRUD operations on Items
|
* An abstract service for CRUD operations on Items
|
||||||
@@ -59,7 +56,6 @@ export abstract class BaseItemDataService extends IdentifiableDataService<Item>
|
|||||||
private createData: CreateData<Item>;
|
private createData: CreateData<Item>;
|
||||||
private patchData: PatchData<Item>;
|
private patchData: PatchData<Item>;
|
||||||
private deleteData: DeleteData<Item>;
|
private deleteData: DeleteData<Item>;
|
||||||
private searchData: SearchDataImpl<Duplicate>;
|
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
protected linkPath,
|
protected linkPath,
|
||||||
@@ -78,7 +74,6 @@ export abstract class BaseItemDataService extends IdentifiableDataService<Item>
|
|||||||
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
|
this.createData = new CreateDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive);
|
||||||
this.patchData = new PatchDataImpl<Item>(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
|
this.patchData = new PatchDataImpl<Item>(this.linkPath, requestService, rdbService, objectCache, halService, comparator, this.responseMsToLive, this.constructIdEndpoint);
|
||||||
this.deleteData = new DeleteDataImpl(this.linkPath, requestService, rdbService, objectCache, halService, notificationsService, this.responseMsToLive, this.constructIdEndpoint);
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -247,20 +242,6 @@ export abstract class BaseItemDataService extends IdentifiableDataService<Item>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public findDuplicates(uuid: string, options?: FindListOptions, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Duplicate>[]): Observable<RemoteData<PaginatedList<Duplicate>>> {
|
|
||||||
const searchParams = [new RequestParam('uuid', uuid)];
|
|
||||||
let findListOptions = new FindListOptions();
|
|
||||||
if (options) {
|
|
||||||
findListOptions = Object.assign(new FindListOptions(), options);
|
|
||||||
}
|
|
||||||
if (findListOptions.searchParams) {
|
|
||||||
findListOptions.searchParams = [...findListOptions.searchParams, ...searchParams];
|
|
||||||
} else {
|
|
||||||
findListOptions.searchParams = searchParams;
|
|
||||||
}
|
|
||||||
return this.searchData.searchBy('findDuplicates', findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the endpoint to move the item
|
* Get the endpoint to move the item
|
||||||
* @param itemId
|
* @param itemId
|
||||||
|
@@ -3,28 +3,34 @@ import { Observable } from 'rxjs';
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||||
import { ResponseParsingService } from './parsing.service';
|
import { ResponseParsingService } from '../data/parsing.service';
|
||||||
import { RemoteData } from './remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import { GetRequest } from './request.models';
|
import { GetRequest } from '../data/request.models';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { GenericConstructor } from '../shared/generic-constructor';
|
import { GenericConstructor } from '../shared/generic-constructor';
|
||||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||||
import { SearchResponseParsingService } from './search-response-parsing.service';
|
import { SearchResponseParsingService } from '../data/search-response-parsing.service';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { RestRequest } from './rest-request.model';
|
import { RestRequest } from '../data/rest-request.model';
|
||||||
import { BaseDataService } from './base/base-data.service';
|
import { BaseDataService } from '../data/base/base-data.service';
|
||||||
import { FindListOptions } from './find-list-options.model';
|
import { FindListOptions } from '../data/find-list-options.model';
|
||||||
import { Duplicate } from '../../shared/object-list/duplicate-data/duplicate.model';
|
import { Duplicate } from '../../shared/object-list/duplicate-data/duplicate.model';
|
||||||
import { PaginatedList } from './paginated-list.model';
|
import { PaginatedList } from '../data/paginated-list.model';
|
||||||
import { RequestParam } from '../cache/models/request-param.model';
|
import { RequestParam } from '../cache/models/request-param.model';
|
||||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that performs all general actions that have to do with the search page
|
* Service that handles search requests for potential duplicate items.
|
||||||
|
* This uses the /api/submission/duplicates endpoint to look for other archived or in-progress items (if user
|
||||||
|
* has READ permission) that match the item (for the given uuid).
|
||||||
|
* Matching is configured in the backend in dspace/config/modulesduplicate-detection.cfg
|
||||||
|
* The returned results are small preview 'stubs' of items, and displayed in either a submission section
|
||||||
|
* or the workflow pooled/claimed task page.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DuplicateDataService extends BaseDataService<Duplicate> {
|
export class SubmissionDuplicateDataService extends BaseDataService<Duplicate> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ResponseParsingService constructor name
|
* The ResponseParsingService constructor name
|
@@ -5,6 +5,10 @@ import { CacheableObject } from '../../../core/cache/cacheable-object.model';
|
|||||||
import { DUPLICATE } from './duplicate.resource-type';
|
import { DUPLICATE } from './duplicate.resource-type';
|
||||||
import { ResourceType } from '../../../core/shared/resource-type';
|
import { ResourceType } from '../../../core/shared/resource-type';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implements the model of a duplicate preview stub, to be displayed to submitters or reviewers
|
||||||
|
* if duplicate detection is enabled. The metadata map is configurable in the backend at duplicate-detection.cfg
|
||||||
|
*/
|
||||||
export class Duplicate implements CacheableObject {
|
export class Duplicate implements CacheableObject {
|
||||||
|
|
||||||
static type = DUPLICATE;
|
static type = DUPLICATE;
|
||||||
@@ -14,17 +18,28 @@ export class Duplicate implements CacheableObject {
|
|||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
title: string;
|
title: string;
|
||||||
|
/**
|
||||||
|
* The item uuid
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
uuid: string;
|
uuid: string;
|
||||||
|
/**
|
||||||
|
* The workfow item ID, if any
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
workflowItemId: number;
|
workflowItemId: number;
|
||||||
|
/**
|
||||||
|
* The workspace item ID, if any
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
workspaceItemId: number;
|
workspaceItemId: number;
|
||||||
|
/**
|
||||||
|
* The owning collection of the item
|
||||||
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
owningCollection: string;
|
owningCollection: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata for the bitstream (e.g. dc.description)
|
* Metadata for the preview item (e.g. dc.title)
|
||||||
*/
|
*/
|
||||||
@autoserialize
|
@autoserialize
|
||||||
metadata: MetadataMap;
|
metadata: MetadataMap;
|
||||||
@@ -33,7 +48,7 @@ export class Duplicate implements CacheableObject {
|
|||||||
type: ResourceType;
|
type: ResourceType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link HALLink}s for this Bitstream
|
* The {@link HALLink}s for the URL that generated this item (in context of search results)
|
||||||
*/
|
*/
|
||||||
@deserialize
|
@deserialize
|
||||||
_links: {
|
_links: {
|
||||||
|
@@ -30,7 +30,9 @@ import { ObjectCacheService } from '../../../../core/cache/object-cache.service'
|
|||||||
import { Context } from '../../../../core/shared/context.model';
|
import { Context } from '../../../../core/shared/context.model';
|
||||||
import { createPaginatedList } from '../../../testing/utils.test';
|
import { createPaginatedList } from '../../../testing/utils.test';
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||||
import { DuplicateDataService } from '../../../../core/data/duplicate-search.service';
|
import { SubmissionDuplicateDataService } from '../../../../core/submission/submission-duplicate-data.service';
|
||||||
|
import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model';
|
||||||
|
import { ConfigurationDataService } from '../../../../core/data/configuration-data.service';
|
||||||
|
|
||||||
let component: ClaimedSearchResultListElementComponent;
|
let component: ClaimedSearchResultListElementComponent;
|
||||||
let fixture: ComponentFixture<ClaimedSearchResultListElementComponent>;
|
let fixture: ComponentFixture<ClaimedSearchResultListElementComponent>;
|
||||||
@@ -41,8 +43,15 @@ mockResultObject.hitHighlights = {};
|
|||||||
const emptyList = createSuccessfulRemoteDataObject(createPaginatedList([]));
|
const emptyList = createSuccessfulRemoteDataObject(createPaginatedList([]));
|
||||||
const itemDataServiceStub = {
|
const itemDataServiceStub = {
|
||||||
findListByHref: () => observableOf(emptyList),
|
findListByHref: () => observableOf(emptyList),
|
||||||
|
|
||||||
};
|
};
|
||||||
|
const configurationDataService = jasmine.createSpyObj('configurationDataService', {
|
||||||
|
findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), {
|
||||||
|
name: 'duplicate.enable',
|
||||||
|
values: [
|
||||||
|
'true'
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
});
|
||||||
const duplicateDataServiceStub = {
|
const duplicateDataServiceStub = {
|
||||||
findListByHref: () => observableOf(emptyList),
|
findListByHref: () => observableOf(emptyList),
|
||||||
findDuplicates: () => createSuccessfulRemoteDataObject$({}),
|
findDuplicates: () => createSuccessfulRemoteDataObject$({}),
|
||||||
@@ -98,7 +107,8 @@ describe('ClaimedSearchResultListElementComponent', () => {
|
|||||||
{ provide: APP_CONFIG, useValue: environment },
|
{ provide: APP_CONFIG, useValue: environment },
|
||||||
{ provide: ObjectCacheService, useValue: objectCacheServiceMock },
|
{ provide: ObjectCacheService, useValue: objectCacheServiceMock },
|
||||||
{ provide: ItemDataService, useValue: itemDataServiceStub },
|
{ provide: ItemDataService, useValue: itemDataServiceStub },
|
||||||
{ provide: DuplicateDataService, useValue: duplicateDataServiceStub },
|
{ provide: ConfigurationDataService, useValue: configurationDataService },
|
||||||
|
{ provide: SubmissionDuplicateDataService, useValue: duplicateDataServiceStub },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(ClaimedSearchResultListElementComponent, {
|
}).overrideComponent(ClaimedSearchResultListElementComponent, {
|
||||||
|
@@ -5,7 +5,7 @@ import { listableObjectComponent } from '../../../object-collection/shared/lista
|
|||||||
import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claimed-task-search-result.model';
|
import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claimed-task-search-result.model';
|
||||||
import { LinkService } from '../../../../core/cache/builders/link.service';
|
import { LinkService } from '../../../../core/cache/builders/link.service';
|
||||||
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
||||||
import { BehaviorSubject, EMPTY, Observable } from 'rxjs';
|
import {BehaviorSubject, combineLatest, EMPTY, Observable} from 'rxjs';
|
||||||
import { RemoteData } from '../../../../core/data/remote-data';
|
import { RemoteData } from '../../../../core/data/remote-data';
|
||||||
import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model';
|
import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model';
|
||||||
import { followLink } from '../../../utils/follow-link-config.model';
|
import { followLink } from '../../../utils/follow-link-config.model';
|
||||||
@@ -24,7 +24,9 @@ import { Context } from '../../../../core/shared/context.model';
|
|||||||
import { Duplicate } from '../../duplicate-data/duplicate.model';
|
import { Duplicate } from '../../duplicate-data/duplicate.model';
|
||||||
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||||
import { DuplicateDataService } from '../../../../core/data/duplicate-search.service';
|
import { SubmissionDuplicateDataService } from '../../../../core/submission/submission-duplicate-data.service';
|
||||||
|
import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model';
|
||||||
|
import { ConfigurationDataService } from '../../../../core/data/configuration-data.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-claimed-search-result-list-element',
|
selector: 'ds-claimed-search-result-list-element',
|
||||||
@@ -57,7 +59,7 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle
|
|||||||
/**
|
/**
|
||||||
* The potential duplicates of this item
|
* The potential duplicates of this item
|
||||||
*/
|
*/
|
||||||
public duplicates$: Observable<Duplicate[]> = new Observable<Duplicate[]>();
|
public duplicates$: Observable<Duplicate[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display thumbnails if required by configuration
|
* Display thumbnails if required by configuration
|
||||||
@@ -70,7 +72,8 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle
|
|||||||
public dsoNameService: DSONameService,
|
public dsoNameService: DSONameService,
|
||||||
protected objectCache: ObjectCacheService,
|
protected objectCache: ObjectCacheService,
|
||||||
protected itemDataService: ItemDataService,
|
protected itemDataService: ItemDataService,
|
||||||
protected duplicateDataService: DuplicateDataService,
|
protected configService: ConfigurationDataService,
|
||||||
|
protected duplicateDataService: SubmissionDuplicateDataService,
|
||||||
@Inject(APP_CONFIG) protected appConfig: AppConfig
|
@Inject(APP_CONFIG) protected appConfig: AppConfig
|
||||||
) {
|
) {
|
||||||
super(truncatableService, dsoNameService, appConfig);
|
super(truncatableService, dsoNameService, appConfig);
|
||||||
@@ -114,10 +117,45 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
).subscribe();
|
).subscribe();
|
||||||
|
// Initialise duplicates, if enabled
|
||||||
|
this.duplicates$ = this.initializeDuplicateDetectionIfEnabled();
|
||||||
this.showThumbnails = this.appConfig.browseBy.showThumbnails;
|
this.showThumbnails = this.appConfig.browseBy.showThumbnails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize and set the duplicates observable based on whether the configuration in REST is enabled
|
||||||
|
* and the results returned
|
||||||
|
*/
|
||||||
|
initializeDuplicateDetectionIfEnabled() {
|
||||||
|
return combineLatest([
|
||||||
|
this.configService.findByPropertyName('duplicate.enable').pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
map((remoteData: RemoteData<ConfigurationProperty>) => {
|
||||||
|
return (remoteData.isSuccess && remoteData.payload && remoteData.payload.values[0] === 'true');
|
||||||
|
})
|
||||||
|
),
|
||||||
|
this.item$.pipe(),
|
||||||
|
]
|
||||||
|
).pipe(
|
||||||
|
map(([enabled, rd]) => {
|
||||||
|
if (enabled) {
|
||||||
|
this.duplicates$ = this.duplicateDataService.findDuplicates(rd.uuid).pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
map((remoteData: RemoteData<PaginatedList<Duplicate>>) => {
|
||||||
|
if (remoteData.hasSucceeded) {
|
||||||
|
if (remoteData.payload.page) {
|
||||||
|
return remoteData.payload.page;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return [] as Duplicate[];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
// This ensures the object is removed from cache, when action is performed on task
|
// This ensures the object is removed from cache, when action is performed on task
|
||||||
if (hasValue(this.dso)) {
|
if (hasValue(this.dso)) {
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
[showSubmitter]="showSubmitter"
|
[showSubmitter]="showSubmitter"
|
||||||
[badgeContext]="badgeContext"
|
[badgeContext]="badgeContext"
|
||||||
[workflowItem]="workflowitem$.value"></ds-themed-item-list-preview>
|
[workflowItem]="workflowitem$.value"></ds-themed-item-list-preview>
|
||||||
|
|
||||||
<!-- Display duplicate alert, if feature enabled and duplicates detected -->
|
<!-- Display duplicate alert, if feature enabled and duplicates detected -->
|
||||||
<ng-container *ngVar="(duplicates$|async)?.length as duplicateCount">
|
<ng-container *ngVar="(duplicates$|async)?.length as duplicateCount">
|
||||||
<div [ngClass]="'row'" *ngIf="duplicateCount > 0">
|
<div [ngClass]="'row'" *ngIf="duplicateCount > 0">
|
||||||
|
@@ -29,7 +29,9 @@ import { ObjectCacheService } from '../../../../core/cache/object-cache.service'
|
|||||||
import { Context } from '../../../../core/shared/context.model';
|
import { Context } from '../../../../core/shared/context.model';
|
||||||
import { createPaginatedList } from '../../../testing/utils.test';
|
import { createPaginatedList } from '../../../testing/utils.test';
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||||
import { DuplicateDataService } from '../../../../core/data/duplicate-search.service';
|
import { SubmissionDuplicateDataService } from '../../../../core/submission/submission-duplicate-data.service';
|
||||||
|
import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model';
|
||||||
|
import { ConfigurationDataService } from '../../../../core/data/configuration-data.service';
|
||||||
|
|
||||||
let component: PoolSearchResultListElementComponent;
|
let component: PoolSearchResultListElementComponent;
|
||||||
let fixture: ComponentFixture<PoolSearchResultListElementComponent>;
|
let fixture: ComponentFixture<PoolSearchResultListElementComponent>;
|
||||||
@@ -41,6 +43,14 @@ const emptyList = createSuccessfulRemoteDataObject(createPaginatedList([]));
|
|||||||
const itemDataServiceStub = {
|
const itemDataServiceStub = {
|
||||||
findListByHref: () => observableOf(emptyList),
|
findListByHref: () => observableOf(emptyList),
|
||||||
};
|
};
|
||||||
|
const configurationDataService = jasmine.createSpyObj('configurationDataService', {
|
||||||
|
findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), {
|
||||||
|
name: 'duplicate.enable',
|
||||||
|
values: [
|
||||||
|
'true'
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
});
|
||||||
const duplicateDataServiceStub = {
|
const duplicateDataServiceStub = {
|
||||||
findListByHref: () => observableOf(emptyList),
|
findListByHref: () => observableOf(emptyList),
|
||||||
findDuplicates: () => createSuccessfulRemoteDataObject$({}),
|
findDuplicates: () => createSuccessfulRemoteDataObject$({}),
|
||||||
@@ -104,7 +114,8 @@ describe('PoolSearchResultListElementComponent', () => {
|
|||||||
{ provide: APP_CONFIG, useValue: environmentUseThumbs },
|
{ provide: APP_CONFIG, useValue: environmentUseThumbs },
|
||||||
{ provide: ObjectCacheService, useValue: objectCacheServiceMock },
|
{ provide: ObjectCacheService, useValue: objectCacheServiceMock },
|
||||||
{ provide: ItemDataService, useValue: itemDataServiceStub },
|
{ provide: ItemDataService, useValue: itemDataServiceStub },
|
||||||
{ provide: DuplicateDataService, useValue: duplicateDataServiceStub }
|
{ provide: ConfigurationDataService, useValue: configurationDataService },
|
||||||
|
{ provide: SubmissionDuplicateDataService, useValue: duplicateDataServiceStub }
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(PoolSearchResultListElementComponent, {
|
}).overrideComponent(PoolSearchResultListElementComponent, {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, EMPTY, Observable } from 'rxjs';
|
import {BehaviorSubject, combineLatest, EMPTY, Observable } from 'rxjs';
|
||||||
import { map, mergeMap, tap } from 'rxjs/operators';
|
import { map, mergeMap, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
@@ -25,7 +25,9 @@ import { Context } from '../../../../core/shared/context.model';
|
|||||||
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../../../core/data/paginated-list.model';
|
||||||
import { Duplicate } from '../../duplicate-data/duplicate.model';
|
import { Duplicate } from '../../duplicate-data/duplicate.model';
|
||||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||||
import { DuplicateDataService } from '../../../../core/data/duplicate-search.service';
|
import { SubmissionDuplicateDataService } from '../../../../core/submission/submission-duplicate-data.service';
|
||||||
|
import { ConfigurationDataService } from '../../../../core/data/configuration-data.service';
|
||||||
|
import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders pool task object for the search result in the list view.
|
* This component renders pool task object for the search result in the list view.
|
||||||
@@ -62,7 +64,7 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen
|
|||||||
/**
|
/**
|
||||||
* The potential duplicates of this workflow item
|
* The potential duplicates of this workflow item
|
||||||
*/
|
*/
|
||||||
public duplicates$: Observable<Duplicate[]> = new Observable<Duplicate[]>();
|
public duplicates$: Observable<Duplicate[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of this list element
|
* The index of this list element
|
||||||
@@ -74,13 +76,16 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen
|
|||||||
*/
|
*/
|
||||||
showThumbnails: boolean;
|
showThumbnails: boolean;
|
||||||
|
|
||||||
|
enableDetectDuplicates$: Observable<any>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected linkService: LinkService,
|
protected linkService: LinkService,
|
||||||
protected truncatableService: TruncatableService,
|
protected truncatableService: TruncatableService,
|
||||||
public dsoNameService: DSONameService,
|
public dsoNameService: DSONameService,
|
||||||
protected objectCache: ObjectCacheService,
|
protected objectCache: ObjectCacheService,
|
||||||
protected itemDataService: ItemDataService,
|
protected itemDataService: ItemDataService,
|
||||||
protected duplicateDataService: DuplicateDataService,
|
protected configService: ConfigurationDataService,
|
||||||
|
protected duplicateDataService: SubmissionDuplicateDataService,
|
||||||
@Inject(APP_CONFIG) protected appConfig: AppConfig
|
@Inject(APP_CONFIG) protected appConfig: AppConfig
|
||||||
) {
|
) {
|
||||||
super(truncatableService, dsoNameService, appConfig);
|
super(truncatableService, dsoNameService, appConfig);
|
||||||
@@ -96,6 +101,14 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen
|
|||||||
followLink('submitter')
|
followLink('submitter')
|
||||||
), followLink('action'));
|
), followLink('action'));
|
||||||
|
|
||||||
|
// Get configuration for duplicate detection feature
|
||||||
|
this.enableDetectDuplicates$ = this.configService.findByPropertyName('duplicate.enable').pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
map((rd: RemoteData<ConfigurationProperty>) => {
|
||||||
|
return (rd.hasSucceeded && rd.payload && rd.payload.values[0] === 'true');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
(this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>).pipe(
|
(this.dso.workflowitem as Observable<RemoteData<WorkflowItem>>).pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
mergeMap((wfiRD: RemoteData<WorkflowItem>) => {
|
mergeMap((wfiRD: RemoteData<WorkflowItem>) => {
|
||||||
@@ -111,8 +124,32 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen
|
|||||||
tap((itemRD: RemoteData<Item>) => {
|
tap((itemRD: RemoteData<Item>) => {
|
||||||
if (isNotEmpty(itemRD) && itemRD.hasSucceeded) {
|
if (isNotEmpty(itemRD) && itemRD.hasSucceeded) {
|
||||||
this.item$.next(itemRD.payload);
|
this.item$.next(itemRD.payload);
|
||||||
// Find duplicates for this item
|
}
|
||||||
this.duplicates$ = this.duplicateDataService.findDuplicates(itemRD.payload.uuid).pipe(
|
}),
|
||||||
|
).subscribe();
|
||||||
|
this.showThumbnails = this.appConfig.browseBy.showThumbnails;
|
||||||
|
// Initialise duplicates, if enabled
|
||||||
|
this.duplicates$ = this.initializeDuplicateDetectionIfEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize and set the duplicates observable based on whether the configuration in REST is enabled
|
||||||
|
* and the results returned
|
||||||
|
*/
|
||||||
|
initializeDuplicateDetectionIfEnabled() {
|
||||||
|
return combineLatest([
|
||||||
|
this.configService.findByPropertyName('duplicate.enable').pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
map((remoteData: RemoteData<ConfigurationProperty>) => {
|
||||||
|
return (remoteData.isSuccess && remoteData.payload && remoteData.payload.values[0] === 'true');
|
||||||
|
})
|
||||||
|
),
|
||||||
|
this.item$.pipe(),
|
||||||
|
]
|
||||||
|
).pipe(
|
||||||
|
map(([enabled, rd]) => {
|
||||||
|
if (enabled) {
|
||||||
|
this.duplicates$ = this.duplicateDataService.findDuplicates(rd.uuid).pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
map((remoteData: RemoteData<PaginatedList<Duplicate>>) => {
|
map((remoteData: RemoteData<PaginatedList<Duplicate>>) => {
|
||||||
if (remoteData.hasSucceeded) {
|
if (remoteData.hasSucceeded) {
|
||||||
@@ -122,12 +159,11 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return [] as Duplicate[];
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
).subscribe();
|
);
|
||||||
|
|
||||||
this.showThumbnails = this.appConfig.browseBy.showThumbnails;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
@@ -76,7 +76,7 @@ const duplicates: Duplicate[] = [{
|
|||||||
type: DUPLICATE,
|
type: DUPLICATE,
|
||||||
_links: {
|
_links: {
|
||||||
self: {
|
self: {
|
||||||
href: 'http://localhost:8080/server/api/core/items/search/findDuplicates?uuid=testid'
|
href: 'http://localhost:8080/server/api/core/submission/duplicates/search?uuid=testid'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
Reference in New Issue
Block a user