Skip to content

Commit 43cf493

Browse files
MatusBekeclaudeMatus Kasak
authored
UFAL/Fix misleading notification when admin self-delete is rejected (#1357)
* Fix misleading message when self-delete rejection lacks a matched error text isSelfDeletionError() matches the backend's rejection message as plain text, but Spring Boot omits exception messages from error response bodies by default and DSpaceBadRequestException/IllegalStateException have no dedicated JSON-body exception handler, so the match can silently fail and fall through to the generic, unfriendly failure notification instead of the "you cannot delete your own account" one. Add a deterministic client-side identity check as a fallback alongside the text match so the friendly message shows reliably regardless of what the backend's error body contains. Fixes dataquest-dev/dspace-customers#782 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Removed redundant comments --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Matus Kasak <matus.kasak@dataquest.sk>
1 parent 390d0a5 commit 43cf493

4 files changed

Lines changed: 36 additions & 4 deletions

File tree

src/app/access-control/epeople-registry/epeople-registry.component.spec.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Router } from '@angular/router';
2-
import { Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
2+
import { defer, Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
33
import { CommonModule } from '@angular/common';
44
import { NO_ERRORS_SCHEMA } from '@angular/core';
55
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
@@ -382,6 +382,23 @@ describe('EPeopleRegistryComponent', () => {
382382
expect(modalService.open).not.toHaveBeenCalled();
383383
expect(deleteSpy).not.toHaveBeenCalled();
384384
}));
385+
386+
it('should still show the friendly self-delete notification if the authenticated user id resolves late and the backend rejection carries no usable message', fakeAsync(() => {
387+
modalRef.componentInstance.response = observableOf(true);
388+
component.currentAuthenticatedUserId = EPersonMock.id;
389+
ePersonDataServiceStub.deleteEPerson = jasmine.createSpy('deleteEPerson').and.returnValue(defer(() => {
390+
component.currentAuthenticatedUserId = EPersonMock2.id;
391+
return createFailedRemoteDataObject$(undefined, 400);
392+
}));
393+
394+
component.deleteEPerson(EPersonMock2);
395+
tick();
396+
397+
expect(notificationsService.error).toHaveBeenCalled();
398+
let translatedKey: string;
399+
notificationsService.error.calls.mostRecent().args[0].subscribe((value) => translatedKey = value);
400+
expect(translatedKey).toBe('admin.access-control.epeople.notification.deleted.forbidden.self');
401+
}));
385402
});
386403

387404
describe('delete EPerson button when the isAuthorized returns false', () => {

src/app/access-control/epeople-registry/epeople-registry.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
224224
this.epersonService.deleteEPerson(ePerson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData<NoContent>) => {
225225
if (restResponse.hasSucceeded) {
226226
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', {name: this.dsoNameService.getName(ePerson)}));
227-
} else if (this.deleteGuard.isSelfDeletionError(restResponse)) {
227+
} else if (this.isCurrentUser(ePerson) || this.deleteGuard.isSelfDeletionError(restResponse)) {
228228
this.deleteGuard.showSelfDeleteNotification();
229229
} else {
230230
this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.deleted.failure', {

src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
1+
import { defer, Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
22
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
33
import { EPersonDeleteGuardService } from '../eperson-delete-guard.service';
44
import { CommonModule } from '@angular/common';
@@ -610,6 +610,21 @@ describe('EPersonFormComponent', () => {
610610
expect(modalService.open).not.toHaveBeenCalled();
611611
expect(deleteSpy).not.toHaveBeenCalled();
612612
});
613+
614+
it('should still show the friendly self-delete notification if the authenticated user id resolves late and the backend rejection carries no usable message', () => {
615+
spyOn(component.epersonService, 'deleteEPerson').and.returnValue(defer(() => {
616+
component.currentAuthenticatedUserId = eperson.id;
617+
return createFailedRemoteDataObject$(undefined, 400);
618+
}));
619+
620+
const deleteButton = fixture.debugElement.query(By.css('.delete-button'));
621+
deleteButton.triggerEventHandler('click', null);
622+
623+
expect(notificationsService.error).toHaveBeenCalled();
624+
let translatedKey: string;
625+
notificationsService.error.calls.mostRecent().args[0].subscribe((value) => translatedKey = value);
626+
expect(translatedKey).toBe('admin.access-control.epeople.notification.deleted.forbidden.self');
627+
});
613628
});
614629

615630
describe('self delete button', () => {

src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
535535
if (restResponse?.hasSucceeded) {
536536
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { name: this.dsoNameService.getName(eperson) }));
537537
void this.router.navigate([getEPersonsRoute()]);
538-
} else if (this.deleteGuard.isSelfDeletionError(restResponse)) {
538+
} else if (this.isCurrentUser(eperson) || this.deleteGuard.isSelfDeletionError(restResponse)) {
539539
this.deleteGuard.showSelfDeleteNotification();
540540
} else {
541541
this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.deleted.failure', {

0 commit comments

Comments
 (0)