From 1853d1bda2451a6ed337e38032d7c4fb5b360c1c Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Fri, 13 Mar 2020 17:28:37 +0100 Subject: [PATCH] 69110: Failure notification on create and edit eperson --- resources/i18n/en.json5 | 2 + .../eperson-form/eperson-form.component.ts | 61 +++++++++++++------ src/app/core/data/data.service.ts | 42 +++++++++++++ src/app/core/eperson/eperson-data.service.ts | 15 +++-- 4 files changed, 97 insertions(+), 23 deletions(-) diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5 index 6b70888484..d9132313f7 100644 --- a/resources/i18n/en.json5 +++ b/resources/i18n/en.json5 @@ -220,6 +220,8 @@ "admin.access-control.epeople.form.notification.created.failure": "Failed to create EPerson \"{{name}}\"", + "admin.access-control.epeople.form.notification.created.failure.emailInUse": "Failed to create EPerson \"{{name}}\", email \"{{email}}\" already in use.", + "admin.access-control.epeople.form.notification.edited.success": "Successfully edited EPerson \"{{name}}\"", "admin.access-control.epeople.form.notification.edited.failure": "Failed to edit EPerson \"{{name}}\"", diff --git a/src/app/+admin/admin-access-control/epeople-registry/eperson-form/eperson-form.component.ts b/src/app/+admin/admin-access-control/epeople-registry/eperson-form/eperson-form.component.ts index 915c4ba67a..b4c0cd311b 100644 --- a/src/app/+admin/admin-access-control/epeople-registry/eperson-form/eperson-form.component.ts +++ b/src/app/+admin/admin-access-control/epeople-registry/eperson-form/eperson-form.component.ts @@ -10,6 +10,8 @@ import { TranslateService } from '@ngx-translate/core'; import { combineLatest } from 'rxjs/internal/observable/combineLatest'; import { Subscription } from 'rxjs/internal/Subscription'; import { take } from 'rxjs/operators'; +import { RestResponse } from '../../../../core/cache/response.models'; +import { PaginatedList } from '../../../../core/data/paginated-list'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { EPerson } from '../../../../core/eperson/models/eperson.model'; import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators'; @@ -231,14 +233,18 @@ export class EPersonFormComponent implements OnInit, OnDestroy { * @param values */ createNewEPerson(values) { - this.subs.push(this.epersonService.create(Object.assign(new EPerson(), values), null) - .pipe( - getSucceededRemoteData(), - getRemoteDataPayload()) - .subscribe((newEPerson: EPerson) => { - this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', { name: newEPerson.name })); - this.submitForm.emit(newEPerson); - })); + const ePersonToCreate = Object.assign(new EPerson(), values); + + const response = this.epersonService.tryToCreate(ePersonToCreate); + response.pipe(take(1)).subscribe((restResponse: RestResponse) => { + if (restResponse.isSuccessful) { + this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', { name: ePersonToCreate.name })); + this.submitForm.emit(ePersonToCreate); + } else { + this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure', { name: ePersonToCreate.name })); + } + }); + this.showNotificationIfEmailInUse(ePersonToCreate); } /** @@ -247,7 +253,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy { * @param values */ editEPerson(ePerson: EPerson, values) { - this.epersonService.updateEPerson(Object.assign(new EPerson(), { + const editedEperson = Object.assign(new EPerson(), { id: ePerson.id, metadata: { 'eperson.firstname': [ @@ -266,14 +272,35 @@ export class EPersonFormComponent implements OnInit, OnDestroy { requireCertificate: (hasValue(values.requireCertificate) ? values.requireCertificate : ePerson.requireCertificate), selfRegistered: false, _links: ePerson._links, - })) - .pipe( - getSucceededRemoteData(), - getRemoteDataPayload()) - .subscribe((updatedEPerson: EPerson) => { - this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.edited.success', { name: updatedEPerson.name })); - this.submitForm.emit(updatedEPerson); - }); + }); + + const response = this.epersonService.updateEPerson(editedEperson); + response.pipe(take(1)).subscribe((restResponse: RestResponse) => { + if (restResponse.isSuccessful) { + this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.edited.success', { name: editedEperson.name })); + this.submitForm.emit(editedEperson); + } else { + this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure', { name: editedEperson.name })); + } + }); + this.showNotificationIfEmailInUse(editedEperson); + } + + private showNotificationIfEmailInUse(ePersonToCreate: EPerson) { + // Relevant message for email in use + // TODO: should be changed to email scope, but byEmail currently not in backend + this.subs.push(this.epersonService.searchByScope(null, ePersonToCreate.email, { + currentPage: 1, + elementsPerPage: 0 + }).pipe(getSucceededRemoteData(), getRemoteDataPayload()) + .subscribe((list: PaginatedList) => { + if (list.totalElements > 0) { + this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure.emailInUse', { + name: ePersonToCreate.name, + email: ePersonToCreate.email + })); + } + })); } /** diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index 5226b1a9f6..85e58bc2c4 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -409,6 +409,48 @@ export abstract class DataService { ) } + /** + * Create a new DSpaceObject on the server, and store the response + * in the object cache, returns observable of the response to determine success + * + * @param {DSpaceObject} dso + * The object to create + */ + tryToCreate(dso: T): Observable { + const requestId = this.requestService.generateRequestId(); + const endpoint$ = this.halService.getEndpoint(this.linkPath).pipe( + isNotEmptyOperator(), + distinctUntilChanged(), + ); + + const serializedDso = new DSpaceSerializer(getClassForType((dso as any).type)).serialize(dso); + + const request$ = endpoint$.pipe( + take(1), + map((endpoint: string) => new CreateRequest(requestId, endpoint, JSON.stringify(serializedDso))) + ); + + // Execute the post request + request$.pipe( + configureRequest(this.requestService) + ).subscribe(); + + return this.fetchResponse(requestId); + } + + /** + * Gets the restResponse from the requestService + * @param requestId + */ + protected fetchResponse(requestId: string): Observable { + return this.requestService.getByUUID(requestId).pipe( + getResponseFromEntry(), + map((response: RestResponse) => { + return response; + }) + ); + } + /** * Delete an existing DSpace Object on the server * @param dsoID The DSpace Object' id to be removed diff --git a/src/app/core/eperson/eperson-data.service.ts b/src/app/core/eperson/eperson-data.service.ts index 687e89d412..8898212e53 100644 --- a/src/app/core/eperson/eperson-data.service.ts +++ b/src/app/core/eperson/eperson-data.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import { createSelector, select, Store } from '@ngrx/store'; import { Operation } from 'fast-json-patch/lib/core'; import { Observable } from 'rxjs'; -import { filter, mergeMap, take } from 'rxjs/operators'; +import { filter, map, take } from 'rxjs/operators'; import { EPeopleRegistryCancelEPersonAction, EPeopleRegistryEditEPersonAction @@ -17,6 +17,7 @@ import { dataService } from '../cache/builders/build-decorators'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { SearchParam } from '../cache/models/search-param.model'; import { ObjectCacheService } from '../cache/object-cache.service'; +import { RestResponse } from '../cache/response.models'; import { DataService } from '../data/data.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; import { PaginatedList } from '../data/paginated-list'; @@ -136,18 +137,20 @@ export class EPersonDataService extends DataService { * The patch is derived from the differences between the given object and its version in the object cache * @param {DSpaceObject} ePerson The given object */ - public updateEPerson(ePerson: EPerson): Observable> { + public updateEPerson(ePerson: EPerson): Observable { + const requestId = this.requestService.generateRequestId(); const oldVersion$ = this.findByHref(ePerson._links.self.href); - return oldVersion$.pipe( + oldVersion$.pipe( getSucceededRemoteData(), getRemoteDataPayload(), - mergeMap((oldEPerson: EPerson) => { + map((oldEPerson: EPerson) => { const operations = this.generateOperations(oldEPerson, ePerson); - const patchRequest = new PatchRequest(this.requestService.generateRequestId(), ePerson._links.self.href, operations); + const patchRequest = new PatchRequest(requestId, ePerson._links.self.href, operations); this.requestService.configure(patchRequest); - return this.findByHref(ePerson._links.self.href); }), ); + + return this.fetchResponse(requestId); } /**