diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.html b/src/app/+item-page/edit-item-page/item-move/item-move.component.html
index 74ca9aae4e..f68cfb0685 100644
--- a/src/app/+item-page/edit-item-page/item-move/item-move.component.html
+++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.html
@@ -5,19 +5,16 @@
{{'item.edit.move.description' | translate}}
@@ -33,16 +30,24 @@
-
-
+
diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts b/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts
index dd91c65e1e..d200891629 100644
--- a/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts
+++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.spec.ts
@@ -21,6 +21,8 @@ import {
createSuccessfulRemoteDataObject$
} from '../../../shared/remote-data.utils';
import { createPaginatedList } from '../../../shared/testing/utils.test';
+import { RequestService } from '../../../core/data/request.service';
+import { getMockRequestService } from '../../../shared/mocks/request.service.mock';
describe('ItemMoveComponent', () => {
let comp: ItemMoveComponent;
@@ -47,18 +49,25 @@ describe('ItemMoveComponent', () => {
name: 'Test collection 2'
});
- const mockItemDataService = jasmine.createSpyObj({
- moveToCollection: createSuccessfulRemoteDataObject$(collection1)
+ let itemDataService;
+
+ const mockItemDataServiceSuccess = jasmine.createSpyObj({
+ moveToCollection: createSuccessfulRemoteDataObject$(collection1),
+ findById: createSuccessfulRemoteDataObject$(mockItem),
});
const mockItemDataServiceFail = jasmine.createSpyObj({
- moveToCollection: createFailedRemoteDataObject$('Internal server error', 500)
+ moveToCollection: createFailedRemoteDataObject$('Internal server error', 500),
+ findById: createSuccessfulRemoteDataObject$(mockItem),
});
const routeStub = {
data: observableOf({
dso: createSuccessfulRemoteDataObject(Object.assign(new Item(), {
- id: 'item1'
+ id: 'item1',
+ owningCollection: createSuccessfulRemoteDataObject$(Object.assign(new Collection(), {
+ id: 'originalOwningCollection',
+ }))
}))
})
};
@@ -79,43 +88,40 @@ describe('ItemMoveComponent', () => {
const notificationsServiceStub = new NotificationsServiceStub();
+ const init = (mockItemDataService) => {
+ itemDataService = mockItemDataService;
+
+ TestBed.configureTestingModule({
+ imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
+ declarations: [ItemMoveComponent],
+ providers: [
+ { provide: ActivatedRoute, useValue: routeStub },
+ { provide: Router, useValue: routerStub },
+ { provide: ItemDataService, useValue: mockItemDataService },
+ { provide: NotificationsService, useValue: notificationsServiceStub },
+ { provide: SearchService, useValue: mockSearchService },
+ { provide: RequestService, useValue: getMockRequestService() },
+ ], schemas: [
+ CUSTOM_ELEMENTS_SCHEMA
+ ]
+ }).compileComponents();
+ fixture = TestBed.createComponent(ItemMoveComponent);
+ comp = fixture.componentInstance;
+ fixture.detectChanges();
+ };
+
describe('ItemMoveComponent success', () => {
beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
- declarations: [ItemMoveComponent],
- providers: [
- { provide: ActivatedRoute, useValue: routeStub },
- { provide: Router, useValue: routerStub },
- { provide: ItemDataService, useValue: mockItemDataService },
- { provide: NotificationsService, useValue: notificationsServiceStub },
- { provide: SearchService, useValue: mockSearchService },
- ], schemas: [
- CUSTOM_ELEMENTS_SCHEMA
- ]
- }).compileComponents();
- fixture = TestBed.createComponent(ItemMoveComponent);
- comp = fixture.componentInstance;
- fixture.detectChanges();
+ init(mockItemDataServiceSuccess);
});
- it('should load suggestions', () => {
- const expected = [
- collection1,
- collection2
- ];
- comp.collectionSearchResults.subscribe((value) => {
- expect(value).toEqual(expected);
- }
- );
- });
it('should get current url ', () => {
expect(comp.getCurrentUrl()).toEqual('fake-url/fake-id/edit');
});
- it('should on click select the correct collection name and id', () => {
+ it('should select the correct collection name and id on click', () => {
const data = collection1;
- comp.onClick(data);
+ comp.selectDso(data);
expect(comp.selectedCollectionName).toEqual('Test collection 1');
expect(comp.selectedCollection).toEqual(collection1);
@@ -128,12 +134,12 @@ describe('ItemMoveComponent', () => {
});
comp.selectedCollectionName = 'selected-collection-id';
comp.selectedCollection = collection1;
- comp.moveCollection();
+ comp.moveToCollection();
- expect(mockItemDataService.moveToCollection).toHaveBeenCalledWith('item-id', collection1);
+ expect(itemDataService.moveToCollection).toHaveBeenCalledWith('item-id', collection1);
});
it('should call notificationsService success message on success', () => {
- comp.moveCollection();
+ comp.moveToCollection();
expect(notificationsServiceStub.success).toHaveBeenCalled();
});
@@ -142,26 +148,11 @@ describe('ItemMoveComponent', () => {
describe('ItemMoveComponent fail', () => {
beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule],
- declarations: [ItemMoveComponent],
- providers: [
- { provide: ActivatedRoute, useValue: routeStub },
- { provide: Router, useValue: routerStub },
- { provide: ItemDataService, useValue: mockItemDataServiceFail },
- { provide: NotificationsService, useValue: notificationsServiceStub },
- { provide: SearchService, useValue: mockSearchService },
- ], schemas: [
- CUSTOM_ELEMENTS_SCHEMA
- ]
- }).compileComponents();
- fixture = TestBed.createComponent(ItemMoveComponent);
- comp = fixture.componentInstance;
- fixture.detectChanges();
+ init(mockItemDataServiceFail);
});
it('should call notificationsService error message on fail', () => {
- comp.moveCollection();
+ comp.moveToCollection();
expect(notificationsServiceStub.error).toHaveBeenCalled();
});
diff --git a/src/app/+item-page/edit-item-page/item-move/item-move.component.ts b/src/app/+item-page/edit-item-page/item-move/item-move.component.ts
index b1ed121b40..b7ab761fe5 100644
--- a/src/app/+item-page/edit-item-page/item-move/item-move.component.ts
+++ b/src/app/+item-page/edit-item-page/item-move/item-move.component.ts
@@ -1,25 +1,21 @@
import { Component, OnInit } from '@angular/core';
-import { first, map } from 'rxjs/operators';
+import { map, switchMap } from 'rxjs/operators';
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
import { RemoteData } from '../../../core/data/remote-data';
-import { DSpaceObject } from '../../../core/shared/dspace-object.model';
-import { PaginatedList } from '../../../core/data/paginated-list.model';
import { Item } from '../../../core/shared/item.model';
import { ActivatedRoute, Router } from '@angular/router';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import {
- getFirstSucceededRemoteData,
- getFirstCompletedRemoteData, getAllSucceededRemoteDataPayload
+ getAllSucceededRemoteDataPayload, getFirstCompletedRemoteData, getFirstSucceededRemoteData, getRemoteDataPayload,
} from '../../../core/shared/operators';
import { ItemDataService } from '../../../core/data/item-data.service';
-import { Observable, of as observableOf } from 'rxjs';
+import { Observable } from 'rxjs';
import { Collection } from '../../../core/shared/collection.model';
-import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { SearchService } from '../../../core/shared/search/search.service';
-import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
-import { SearchResult } from '../../../shared/search/search-result.model';
import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths';
+import { followLink } from '../../../shared/utils/follow-link-config.model';
+import { RequestService } from '../../../core/data/request.service';
@Component({
selector: 'ds-item-move',
@@ -38,7 +34,8 @@ export class ItemMoveComponent implements OnInit {
inheritPolicies = false;
itemRD$: Observable>;
- collectionSearchResults: Observable = observableOf([]);
+ originalCollection: Collection;
+
selectedCollectionName: string;
selectedCollection: Collection;
canSubmit = false;
@@ -46,23 +43,26 @@ export class ItemMoveComponent implements OnInit {
item: Item;
processing = false;
- pagination = new PaginationComponentOptions();
-
/**
* Route to the item's page
*/
itemPageRoute$: Observable;
+ COLLECTIONS = [DSpaceObjectType.COLLECTION];
+
constructor(private route: ActivatedRoute,
private router: Router,
private notificationsService: NotificationsService,
private itemDataService: ItemDataService,
private searchService: SearchService,
- private translateService: TranslateService) {
- }
+ private translateService: TranslateService,
+ private requestService: RequestService,
+ ) {}
ngOnInit(): void {
- this.itemRD$ = this.route.data.pipe(map((data) => data.dso), getFirstSucceededRemoteData()) as Observable>;
+ this.itemRD$ = this.route.data.pipe(
+ map((data) => data.dso), getFirstSucceededRemoteData()
+ ) as Observable>;
this.itemPageRoute$ = this.itemRD$.pipe(
getAllSucceededRemoteDataPayload(),
map((item) => getItemPageRoute(item))
@@ -71,43 +71,22 @@ export class ItemMoveComponent implements OnInit {
this.item = rd.payload;
}
);
- this.pagination.pageSize = 5;
- this.loadSuggestions('');
- }
-
- /**
- * Find suggestions based on entered query
- * @param query - Search query
- */
- findSuggestions(query): void {
- this.loadSuggestions(query);
- }
-
- /**
- * Load all available collections to move the item to.
- * TODO: When the API support it, only fetch collections where user has ADD rights to.
- */
- loadSuggestions(query): void {
- this.collectionSearchResults = this.searchService.search(new PaginatedSearchOptions({
- pagination: this.pagination,
- dsoTypes: [DSpaceObjectType.COLLECTION],
- query: query
- })).pipe(
- first(),
- map((rd: RemoteData>>) => {
- return rd.payload.page.map((searchResult) => {
- return searchResult.indexableObject;
- });
- }) ,
- );
-
+ this.itemRD$.pipe(
+ getFirstSucceededRemoteData(),
+ getRemoteDataPayload(),
+ switchMap((item) => item.owningCollection),
+ getFirstSucceededRemoteData(),
+ getRemoteDataPayload(),
+ ).subscribe((collection) => {
+ this.originalCollection = collection;
+ });
}
/**
* Set the collection name and id based on the selected value
* @param data - obtained from the ds-input-suggestions component
*/
- onClick(data: any): void {
+ selectDso(data: any): void {
this.selectedCollection = data;
this.selectedCollectionName = data.name;
this.canSubmit = true;
@@ -123,26 +102,41 @@ export class ItemMoveComponent implements OnInit {
/**
* Moves the item to a new collection based on the selected collection
*/
- moveCollection() {
+ moveToCollection() {
this.processing = true;
- this.itemDataService.moveToCollection(this.item.id, this.selectedCollection).pipe(getFirstCompletedRemoteData()).subscribe(
- (response: RemoteData) => {
- this.router.navigate([getItemEditRoute(this.item)]);
- if (response.hasSucceeded) {
- this.notificationsService.success(this.translateService.get('item.edit.move.success'));
- } else {
- this.notificationsService.error(this.translateService.get('item.edit.move.error'));
- }
- this.processing = false;
+ const move$ = this.itemDataService.moveToCollection(this.item.id, this.selectedCollection)
+ .pipe(getFirstCompletedRemoteData());
+
+ move$.subscribe((response: RemoteData) => {
+ if (response.hasSucceeded) {
+ this.notificationsService.success(this.translateService.get('item.edit.move.success'));
+ } else {
+ this.notificationsService.error(this.translateService.get('item.edit.move.error'));
}
- );
+ });
+
+ move$.pipe(
+ switchMap(() => this.requestService.setStaleByHrefSubstring(this.item.id)),
+ switchMap(() =>
+ this.itemDataService.findById(
+ this.item.id,
+ false,
+ true,
+ followLink('owningCollection')
+ )),
+ getFirstCompletedRemoteData()
+ ).subscribe(() => {
+ this.processing = false;
+ this.router.navigate([getItemEditRoute(this.item)]);
+ });
}
- /**
- * Resets the can submit when the user changes the content of the input field
- * @param data
- */
- resetCollection(data: any) {
+ discard(): void {
+ this.selectedCollection = null;
this.canSubmit = false;
}
+
+ get canMove(): boolean {
+ return this.canSubmit && this.selectedCollection?.id !== this.originalCollection.id;
+ }
}
diff --git a/src/app/core/data/item-data.service.ts b/src/app/core/data/item-data.service.ts
index e56f9f2b0c..7a0116fe86 100644
--- a/src/app/core/data/item-data.service.ts
+++ b/src/app/core/data/item-data.service.ts
@@ -23,14 +23,7 @@ import { DataService } from './data.service';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { PaginatedList } from './paginated-list.model';
import { RemoteData } from './remote-data';
-import {
- DeleteRequest,
- FindListOptions,
- GetRequest,
- PostRequest,
- PutRequest,
- RestRequest
-} from './request.models';
+import { DeleteRequest, FindListOptions, GetRequest, PostRequest, PutRequest, RestRequest } from './request.models';
import { RequestService } from './request.service';
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
import { Bundle } from '../shared/bundle.model';
@@ -38,6 +31,9 @@ import { MetadataMap } from '../shared/metadata.models';
import { BundleDataService } from './bundle-data.service';
import { Operation } from 'fast-json-patch';
import { NoContent } from '../shared/NoContent.model';
+import { GenericConstructor } from '../shared/generic-constructor';
+import { ResponseParsingService } from './parsing.service';
+import { StatusCodeOnlyResponseParsingService } from './status-code-only-response-parsing.service';
@Injectable()
@dataService(ITEM)
@@ -229,7 +225,7 @@ export class ItemDataService extends DataService- {
* @param itemId
* @param collection
*/
- public moveToCollection(itemId: string, collection: Collection): Observable> {
+ public moveToCollection(itemId: string, collection: Collection): Observable> {
const options: HttpOptions = Object.create({});
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'text/uri-list');
@@ -242,9 +238,17 @@ export class ItemDataService extends DataService
- {
find((href: string) => hasValue(href)),
map((href: string) => {
const request = new PutRequest(requestId, href, collection._links.self.href, options);
- this.requestService.send(request);
+ Object.assign(request, {
+ // TODO: for now, the move Item endpoint returns a malformed collection -- only look at the status code
+ getResponseParser(): GenericConstructor {
+ return StatusCodeOnlyResponseParsingService;
+ }
+ });
+ return request;
})
- ).subscribe();
+ ).subscribe((request) => {
+ this.requestService.send(request);
+ });
return this.rdbService.buildFromRequestUUID(requestId);
}
diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html
index 122f37b031..ab2ea6cd8b 100644
--- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html
+++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html
@@ -14,12 +14,12 @@
[infiniteScrollContainer]="'.scrollable-menu'"
[fromRoot]="true"
(scrolled)="onScrollDown()">
-
+
-