mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
[TLC-674] Refactor duplicates from item link to searchBy
This commit is contained in:
@@ -46,6 +46,9 @@ 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
|
||||||
@@ -56,6 +59,7 @@ 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,
|
||||||
@@ -74,6 +78,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -242,24 +247,18 @@ export abstract class BaseItemDataService extends IdentifiableDataService<Item>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDuplicatesEndpoint(itemId: string): Observable<string> {
|
public findDuplicates(uuid: string, options?: FindListOptions, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Duplicate>[]): Observable<RemoteData<PaginatedList<Duplicate>>> {
|
||||||
return this.halService.getEndpoint(this.linkPath).pipe(
|
const searchParams = [new RequestParam('uuid', uuid)];
|
||||||
switchMap((url: string) => this.halService.getEndpoint('duplicates', `${url}/${itemId}`))
|
let findListOptions = new FindListOptions();
|
||||||
);
|
if (options) {
|
||||||
|
findListOptions = Object.assign(new FindListOptions(), options);
|
||||||
}
|
}
|
||||||
|
if (findListOptions.searchParams) {
|
||||||
public getDuplicates(itemId: string, searchOptions?: PaginatedSearchOptions): Observable<RemoteData<PaginatedList<Item>>> {
|
findListOptions.searchParams = [...findListOptions.searchParams, ...searchParams];
|
||||||
const hrefObs = this.getDuplicatesEndpoint(itemId).pipe(
|
} else {
|
||||||
map((href) => searchOptions ? searchOptions.toRestUrl(href) : href)
|
findListOptions.searchParams = searchParams;
|
||||||
);
|
}
|
||||||
hrefObs.pipe(
|
return this.searchData.searchBy('findDuplicates', findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||||
take(1)
|
|
||||||
).subscribe((href) => {
|
|
||||||
const request = new GetRequest(this.requestService.generateRequestId(), href);
|
|
||||||
this.requestService.send(request);
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.rdbService.buildList<Item>(hrefObs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -26,7 +26,6 @@ import { AccessStatusObject } from 'src/app/shared/object-collection/shared/badg
|
|||||||
import { HandleObject } from './handle-object.model';
|
import { HandleObject } from './handle-object.model';
|
||||||
import { IDENTIFIERS } from '../../shared/object-list/identifier-data/identifier-data.resource-type';
|
import { IDENTIFIERS } from '../../shared/object-list/identifier-data/identifier-data.resource-type';
|
||||||
import { IdentifierData } from '../../shared/object-list/identifier-data/identifier-data.model';
|
import { IdentifierData } from '../../shared/object-list/identifier-data/identifier-data.model';
|
||||||
import { Duplicate } from '../../shared/object-list/duplicate-data/duplicate.model';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing a DSpace Item
|
* Class representing a DSpace Item
|
||||||
@@ -80,7 +79,6 @@ export class Item extends DSpaceObject implements ChildHALResource, HandleObject
|
|||||||
thumbnail: HALLink;
|
thumbnail: HALLink;
|
||||||
accessStatus: HALLink;
|
accessStatus: HALLink;
|
||||||
identifiers: HALLink;
|
identifiers: HALLink;
|
||||||
duplicates: HALLink;
|
|
||||||
self: HALLink;
|
self: HALLink;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -133,9 +131,6 @@ export class Item extends DSpaceObject implements ChildHALResource, HandleObject
|
|||||||
@link(IDENTIFIERS, false, 'identifiers')
|
@link(IDENTIFIERS, false, 'identifiers')
|
||||||
identifiers?: Observable<RemoteData<IdentifierData>>;
|
identifiers?: Observable<RemoteData<IdentifierData>>;
|
||||||
|
|
||||||
@link(ITEM, true, 'duplicates')
|
|
||||||
duplicates?: Observable<RemoteData<PaginatedList<Duplicate>>>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that returns as which type of object this object should be rendered
|
* Method that returns as which type of object this object should be rendered
|
||||||
*/
|
*/
|
||||||
|
@@ -1,7 +1,14 @@
|
|||||||
import { autoserialize } from 'cerialize';
|
import {autoserialize, deserialize} from 'cerialize';
|
||||||
import { MetadataMap } from '../../../core/shared/metadata.models';
|
import { MetadataMap } from '../../../core/shared/metadata.models';
|
||||||
|
import { HALLink} from '../../../core/shared/hal-link.model';
|
||||||
|
import { CacheableObject } from '../../../core/cache/cacheable-object.model';
|
||||||
|
import { DUPLICATE } from './duplicate.resource-type';
|
||||||
|
import { ResourceType } from '../../../core/shared/resource-type';
|
||||||
|
|
||||||
|
export class Duplicate implements CacheableObject {
|
||||||
|
|
||||||
|
static type = DUPLICATE;
|
||||||
|
|
||||||
export class Duplicate {
|
|
||||||
/**
|
/**
|
||||||
* The item title
|
* The item title
|
||||||
*/
|
*/
|
||||||
@@ -23,5 +30,13 @@ export class Duplicate {
|
|||||||
metadata: MetadataMap;
|
metadata: MetadataMap;
|
||||||
|
|
||||||
@autoserialize
|
@autoserialize
|
||||||
type: string;
|
type: ResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link HALLink}s for this Bitstream
|
||||||
|
*/
|
||||||
|
@deserialize
|
||||||
|
_links: {
|
||||||
|
self: HALLink;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ import { Item } from '../../../../core/shared/item.model';
|
|||||||
import { ClaimedSearchResultListElementComponent } from './claimed-search-result-list-element.component';
|
import { ClaimedSearchResultListElementComponent } from './claimed-search-result-list-element.component';
|
||||||
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
|
||||||
import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model';
|
import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model';
|
||||||
import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils';
|
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils';
|
||||||
import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claimed-task-search-result.model';
|
import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claimed-task-search-result.model';
|
||||||
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
||||||
import { VarDirective } from '../../../utils/var.directive';
|
import { VarDirective } from '../../../utils/var.directive';
|
||||||
@@ -28,6 +28,8 @@ import { APP_CONFIG } from '../../../../../config/app-config.interface';
|
|||||||
import { environment } from '../../../../../environments/environment';
|
import { environment } from '../../../../../environments/environment';
|
||||||
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
|
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 { ItemDataService } from '../../../../core/data/item-data.service';
|
||||||
|
|
||||||
let component: ClaimedSearchResultListElementComponent;
|
let component: ClaimedSearchResultListElementComponent;
|
||||||
let fixture: ComponentFixture<ClaimedSearchResultListElementComponent>;
|
let fixture: ComponentFixture<ClaimedSearchResultListElementComponent>;
|
||||||
@@ -35,6 +37,12 @@ let fixture: ComponentFixture<ClaimedSearchResultListElementComponent>;
|
|||||||
const mockResultObject: ClaimedTaskSearchResult = new ClaimedTaskSearchResult();
|
const mockResultObject: ClaimedTaskSearchResult = new ClaimedTaskSearchResult();
|
||||||
mockResultObject.hitHighlights = {};
|
mockResultObject.hitHighlights = {};
|
||||||
|
|
||||||
|
const emptyList = createSuccessfulRemoteDataObject(createPaginatedList([]));
|
||||||
|
const itemDataServiceStub = {
|
||||||
|
findDuplicates: () => createSuccessfulRemoteDataObject$({}),
|
||||||
|
findListByHref: () => observableOf(emptyList),
|
||||||
|
};
|
||||||
|
|
||||||
const item = Object.assign(new Item(), {
|
const item = Object.assign(new Item(), {
|
||||||
bundles: observableOf({}),
|
bundles: observableOf({}),
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -83,7 +91,8 @@ describe('ClaimedSearchResultListElementComponent', () => {
|
|||||||
{ provide: LinkService, useValue: linkService },
|
{ provide: LinkService, useValue: linkService },
|
||||||
{ provide: DSONameService, useClass: DSONameServiceMock },
|
{ provide: DSONameService, useClass: DSONameServiceMock },
|
||||||
{ provide: APP_CONFIG, useValue: environment },
|
{ provide: APP_CONFIG, useValue: environment },
|
||||||
{ provide: ObjectCacheService, useValue: objectCacheServiceMock }
|
{ provide: ObjectCacheService, useValue: objectCacheServiceMock },
|
||||||
|
{ provide: ItemDataService, useValue: itemDataServiceStub },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(ClaimedSearchResultListElementComponent, {
|
}).overrideComponent(ClaimedSearchResultListElementComponent, {
|
||||||
|
@@ -23,6 +23,7 @@ import { isNotEmpty, hasValue } from '../../../empty.util';
|
|||||||
import { Context } from '../../../../core/shared/context.model';
|
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';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-claimed-search-result-list-element',
|
selector: 'ds-claimed-search-result-list-element',
|
||||||
@@ -67,6 +68,7 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle
|
|||||||
protected truncatableService: TruncatableService,
|
protected truncatableService: TruncatableService,
|
||||||
public dsoNameService: DSONameService,
|
public dsoNameService: DSONameService,
|
||||||
protected objectCache: ObjectCacheService,
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected itemDataService: ItemDataService,
|
||||||
@Inject(APP_CONFIG) protected appConfig: AppConfig
|
@Inject(APP_CONFIG) protected appConfig: AppConfig
|
||||||
) {
|
) {
|
||||||
super(truncatableService, dsoNameService, appConfig);
|
super(truncatableService, dsoNameService, appConfig);
|
||||||
@@ -97,7 +99,7 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle
|
|||||||
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);
|
||||||
this.duplicates$ = itemRD.payload.duplicates.pipe(
|
this.duplicates$ = this.itemDataService.findDuplicates(itemRD.payload.uuid).pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
map((remoteData: RemoteData<PaginatedList<Duplicate>>) => {
|
map((remoteData: RemoteData<PaginatedList<Duplicate>>) => {
|
||||||
if (remoteData.hasSucceeded) {
|
if (remoteData.hasSucceeded) {
|
||||||
|
@@ -15,7 +15,7 @@ import { Item } from '../../../../core/shared/item.model';
|
|||||||
import { PoolSearchResultListElementComponent } from './pool-search-result-list-element.component';
|
import { PoolSearchResultListElementComponent } from './pool-search-result-list-element.component';
|
||||||
import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model';
|
import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model';
|
||||||
import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model';
|
import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model';
|
||||||
import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils';
|
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils';
|
||||||
import { PoolTaskSearchResult } from '../../../object-collection/shared/pool-task-search-result.model';
|
import { PoolTaskSearchResult } from '../../../object-collection/shared/pool-task-search-result.model';
|
||||||
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
import { TruncatableService } from '../../../truncatable/truncatable.service';
|
||||||
import { VarDirective } from '../../../utils/var.directive';
|
import { VarDirective } from '../../../utils/var.directive';
|
||||||
@@ -27,6 +27,8 @@ import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock';
|
|||||||
import { APP_CONFIG } from '../../../../../config/app-config.interface';
|
import { APP_CONFIG } from '../../../../../config/app-config.interface';
|
||||||
import { ObjectCacheService } from '../../../../core/cache/object-cache.service';
|
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 { ItemDataService } from '../../../../core/data/item-data.service';
|
||||||
|
|
||||||
let component: PoolSearchResultListElementComponent;
|
let component: PoolSearchResultListElementComponent;
|
||||||
let fixture: ComponentFixture<PoolSearchResultListElementComponent>;
|
let fixture: ComponentFixture<PoolSearchResultListElementComponent>;
|
||||||
@@ -34,6 +36,12 @@ let fixture: ComponentFixture<PoolSearchResultListElementComponent>;
|
|||||||
const mockResultObject: PoolTaskSearchResult = new PoolTaskSearchResult();
|
const mockResultObject: PoolTaskSearchResult = new PoolTaskSearchResult();
|
||||||
mockResultObject.hitHighlights = {};
|
mockResultObject.hitHighlights = {};
|
||||||
|
|
||||||
|
const emptyList = createSuccessfulRemoteDataObject(createPaginatedList([]));
|
||||||
|
const itemDataServiceStub = {
|
||||||
|
findDuplicates: () => createSuccessfulRemoteDataObject$({}),
|
||||||
|
findListByHref: () => observableOf(emptyList),
|
||||||
|
};
|
||||||
|
|
||||||
const item = Object.assign(new Item(), {
|
const item = Object.assign(new Item(), {
|
||||||
duplicates: observableOf([]),
|
duplicates: observableOf([]),
|
||||||
bundles: observableOf({}),
|
bundles: observableOf({}),
|
||||||
@@ -90,7 +98,8 @@ describe('PoolSearchResultListElementComponent', () => {
|
|||||||
{ provide: LinkService, useValue: linkService },
|
{ provide: LinkService, useValue: linkService },
|
||||||
{ provide: DSONameService, useClass: DSONameServiceMock },
|
{ provide: DSONameService, useClass: DSONameServiceMock },
|
||||||
{ provide: APP_CONFIG, useValue: environmentUseThumbs },
|
{ provide: APP_CONFIG, useValue: environmentUseThumbs },
|
||||||
{ provide: ObjectCacheService, useValue: objectCacheServiceMock }
|
{ provide: ObjectCacheService, useValue: objectCacheServiceMock },
|
||||||
|
{ provide: ItemDataService, useValue: itemDataServiceStub },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(PoolSearchResultListElementComponent, {
|
}).overrideComponent(PoolSearchResultListElementComponent, {
|
||||||
|
@@ -24,6 +24,7 @@ import { isNotEmpty, hasValue } from '../../../empty.util';
|
|||||||
import { Context } from '../../../../core/shared/context.model';
|
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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@@ -77,6 +78,7 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen
|
|||||||
protected truncatableService: TruncatableService,
|
protected truncatableService: TruncatableService,
|
||||||
public dsoNameService: DSONameService,
|
public dsoNameService: DSONameService,
|
||||||
protected objectCache: ObjectCacheService,
|
protected objectCache: ObjectCacheService,
|
||||||
|
protected itemDataService: ItemDataService,
|
||||||
@Inject(APP_CONFIG) protected appConfig: AppConfig
|
@Inject(APP_CONFIG) protected appConfig: AppConfig
|
||||||
) {
|
) {
|
||||||
super(truncatableService, dsoNameService, appConfig);
|
super(truncatableService, dsoNameService, appConfig);
|
||||||
@@ -88,7 +90,7 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
this.linkService.resolveLinks(this.dso, followLink('workflowitem', {},
|
this.linkService.resolveLinks(this.dso, followLink('workflowitem', {},
|
||||||
followLink('item', {}, followLink('bundles'), followLink('duplicates')),
|
followLink('item', {}, followLink('bundles')),
|
||||||
followLink('submitter')
|
followLink('submitter')
|
||||||
), followLink('action'));
|
), followLink('action'));
|
||||||
|
|
||||||
@@ -107,7 +109,7 @@ 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);
|
||||||
this.duplicates$ = itemRD.payload.duplicates.pipe(
|
this.duplicates$ = this.itemDataService.findDuplicates(itemRD.payload.uuid).pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
map((remoteData: RemoteData<PaginatedList<Duplicate>>) => {
|
map((remoteData: RemoteData<PaginatedList<Duplicate>>) => {
|
||||||
if (remoteData.hasSucceeded) {
|
if (remoteData.hasSucceeded) {
|
||||||
|
Reference in New Issue
Block a user