55946: Functionality for removing mappings

This commit is contained in:
Kristof De Langhe
2018-10-10 16:15:53 +02:00
parent dd38e61230
commit 378fbe86f4
5 changed files with 90 additions and 19 deletions

View File

@@ -135,19 +135,34 @@
"head": "Item Mapper - Map Item to Collections",
"item": "Item: \"<b>{{name}}</b>\"",
"description": "This is the item mapper tool that allows administrators to map this item to other collections. You can search for collections and map them, or browse the list of collections the item is currently mapped to.",
"confirm": "Map item to selected collections",
"tabs": {
"browse": "Browse",
"map": "Map"
},
"buttons": {
"add": "Map item to selected collections",
"remove": "Remove item's mapping for selected collections"
},
"notifications": {
"success": {
"head": "Mapping completed",
"content": "Successfully mapped item to {{amount}} collections."
"add": {
"success": {
"head": "Mapping completed",
"content": "Successfully mapped item to {{amount}} collections."
},
"error": {
"head": "Mapping errors",
"content": "Errors occurred for mapping of item to {{amount}} collections."
}
},
"error": {
"head": "Mapping errors",
"content": "Errors occurred for mapping of item to {{amount}} collections."
"remove": {
"success": {
"head": "Removal of mapping completed",
"content": "Successfully removed mapping of item to {{amount}} collections."
},
"error": {
"head": "Removal of mapping errors",
"content": "Errors occurred for the removal of the mapping to {{amount}} collections."
}
}
},
"return": "Return"

View File

@@ -19,7 +19,11 @@
<ngb-tab title="{{'item.edit.item-mapper.tabs.browse' | translate}}">
<ng-template ngbTabContent>
<div class="mt-2">
<div *ngFor="let col of (itemCollectionsRD$ | async)?.payload">{{col.name}}</div>
<ds-collection-select class="mt-2"
[dsoRD$]="itemCollectionsRD$"
[paginationOptions]="(searchOptions$ | async)?.pagination"
[confirmButton]="'item.edit.item-mapper.buttons.remove'"
(confirm)="removeMappings($event)"></ds-collection-select>
</div>
</ng-template>
</ngb-tab>
@@ -29,7 +33,7 @@
<ds-collection-select class="mt-2"
[dsoRD$]="mappingCollectionsRD$"
[paginationOptions]="(searchOptions$ | async)?.pagination"
[confirmButton]="'item.edit.item-mapper.confirm'"
[confirmButton]="'item.edit.item-mapper.buttons.add'"
(confirm)="mapCollections($event)"></ds-collection-select>
</div>
</ng-template>

View File

@@ -18,6 +18,7 @@ import { ItemDataService } from '../../../core/data/item-data.service';
import { RestResponse } from '../../../core/cache/response-cache.models';
import { TranslateService } from '@ngx-translate/core';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { C } from '@angular/core/src/render3';
@Component({
selector: 'ds-item-collection-mapper',
@@ -47,7 +48,7 @@ export class ItemCollectionMapperComponent implements OnInit {
* List of collections to show under the "Browse" tab
* Collections that are mapped to the item
*/
itemCollectionsRD$: Observable<RemoteData<Collection[]>>;
itemCollectionsRD$: Observable<RemoteData<PaginatedList<Collection>>>;
/**
* List of collections to show under the "Map" tab
@@ -97,13 +98,36 @@ export class ItemCollectionMapperComponent implements OnInit {
switchMap((itemId: string) => Observable.combineLatest(ids.map((id: string) => this.itemDataService.mapToCollection(itemId, id))))
);
this.showNotifications(responses$, 'item.edit.item-mapper.notifications.add');
}
/**
* Remove the mapping of the item to the selected collections and display notifications
* @param {string[]} ids The list of collection UUID's to remove the mapping of the item for
*/
removeMappings(ids: string[]) {
const responses$ = this.itemRD$.pipe(
getSucceededRemoteData(),
map((itemRD: RemoteData<Item>) => itemRD.payload.id),
switchMap((itemId: string) => Observable.combineLatest(ids.map((id: string) => this.itemDataService.removeMappingFromCollection(itemId, id))))
);
this.showNotifications(responses$, 'item.edit.item-mapper.notifications.remove');
}
/**
* Display notifications
* @param {Observable<RestResponse[]>} responses$ The responses after adding/removing a mapping
* @param {string} messagePrefix The prefix to build the notification messages with
*/
private showNotifications(responses$: Observable<RestResponse[]>, messagePrefix: string) {
responses$.subscribe((responses: RestResponse[]) => {
const successful = responses.filter((response: RestResponse) => response.isSuccessful);
const unsuccessful = responses.filter((response: RestResponse) => !response.isSuccessful);
if (successful.length > 0) {
const successMessages = Observable.combineLatest(
this.translateService.get('item.edit.item-mapper.notifications.success.head'),
this.translateService.get('item.edit.item-mapper.notifications.success.content', { amount: successful.length })
this.translateService.get(`${messagePrefix}.success.head`),
this.translateService.get(`${messagePrefix}.success.content`, { amount: successful.length })
);
successMessages.subscribe(([head, content]) => {
@@ -112,8 +136,8 @@ export class ItemCollectionMapperComponent implements OnInit {
}
if (unsuccessful.length > 0) {
const unsuccessMessages = Observable.combineLatest(
this.translateService.get('item.edit.item-mapper.notifications.error.head'),
this.translateService.get('item.edit.item-mapper.notifications.error.content', { amount: unsuccessful.length })
this.translateService.get(`${messagePrefix}.error.head`),
this.translateService.get(`${messagePrefix}.error.content`, { amount: unsuccessful.length })
);
unsuccessMessages.subscribe(([head, content]) => {

View File

@@ -15,7 +15,14 @@ import { URLCombiner } from '../url-combiner/url-combiner';
import { DataService } from './data.service';
import { RequestService } from './request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { FindAllOptions, GetRequest, MappingCollectionsRequest, PostRequest, RestRequest } from './request.models';
import {
DeleteRequest,
FindAllOptions,
GetRequest,
MappingCollectionsRequest,
PostRequest,
RestRequest
} from './request.models';
import { distinctUntilChanged, map } from 'rxjs/operators';
import {
configureRequest,
@@ -69,6 +76,18 @@ export class ItemDataService extends DataService<NormalizedItem, Item> {
);
}
public removeMappingFromCollection(itemId: string, collectionId: string): Observable<RestResponse> {
return this.getMappingCollectionsEndpoint(itemId, collectionId).pipe(
isNotEmptyOperator(),
distinctUntilChanged(),
map((endpointURL: string) => new DeleteRequest(this.requestService.generateRequestId(), endpointURL)),
configureRequest(this.requestService),
map((request: RestRequest) => request.href),
getResponseFromSelflink(this.responseCache),
map((responseCacheEntry: ResponseCacheEntry) => responseCacheEntry.response)
);
}
public mapToCollection(itemId: string, collectionId: string): Observable<RestResponse> {
return this.getMappingCollectionsEndpoint(itemId, collectionId).pipe(
isNotEmptyOperator(),
@@ -81,7 +100,7 @@ export class ItemDataService extends DataService<NormalizedItem, Item> {
);
}
public getMappedCollections(itemId: string): Observable<RemoteData<Collection[]>> {
public getMappedCollections(itemId: string): Observable<RemoteData<PaginatedList<Collection>>> {
const request$ = this.getMappingCollectionsEndpoint(itemId).pipe(
isNotEmptyOperator(),
distinctUntilChanged(),
@@ -95,8 +114,7 @@ export class ItemDataService extends DataService<NormalizedItem, Item> {
const payload$ = responseCache$.pipe(
filterSuccessfulResponses(),
map((entry: ResponseCacheEntry) => entry.response),
map((response: GenericSuccessResponse<Collection[]>) => response.payload),
ensureArrayHasValue()
map((response: GenericSuccessResponse<PaginatedList<Collection>>) => response.payload)
);
return this.rdbService.toRemoteDataObservable(requestEntry$, responseCache$, payload$);

View File

@@ -3,6 +3,8 @@ import { ResponseParsingService } from './parsing.service';
import { RestRequest } from './request.models';
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
import { ErrorResponse, GenericSuccessResponse, RestResponse } from '../cache/response-cache.models';
import { PaginatedList } from './paginated-list';
import { PageInfo } from '../shared/page-info.model';
@Injectable()
export class MappingCollectionsReponseParsingService implements ResponseParsingService {
@@ -11,7 +13,15 @@ export class MappingCollectionsReponseParsingService implements ResponseParsingS
if (payload._embedded && payload._embedded.mappingCollections) {
const mappingCollections = payload._embedded.mappingCollections;
return new GenericSuccessResponse(mappingCollections, data.statusCode);
// TODO: When the API supports it, change this to fetch a paginated list, instead of creating static one
// Reason: Pagination is currently not supported on the mappingCollections endpoint
const paginatedMappingCollections = new PaginatedList(Object.assign(new PageInfo(), {
elementsPerPage: mappingCollections.length,
totalElements: mappingCollections.length,
totalPages: 1,
currentPage: 1
}), mappingCollections);
return new GenericSuccessResponse(paginatedMappingCollections, data.statusCode);
} else {
return new ErrorResponse(
Object.assign(