From 172f9ee183dfd6df4930d4f7fee2493988b8b9ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=A4hnlein?= Date: Wed, 27 May 2020 10:22:42 +0200 Subject: [PATCH] feat: delete B2B users in organization-management Closes #258 --- .../facades/organization-management.facade.ts | 5 ++++ .../app/pages/users/users-page.component.html | 25 ++++++++++++++++- .../pages/users/users-page.component.spec.ts | 10 ++++++- .../app/pages/users/users-page.component.ts | 4 +++ .../app/services/users/users.service.spec.ts | 13 +++++++++ .../src/app/services/users/users.service.ts | 13 +++++++++ .../src/app/store/users/users.actions.ts | 6 ++++ .../src/app/store/users/users.effects.spec.ts | 28 +++++++++++++++++++ .../src/app/store/users/users.effects.ts | 17 ++++++++++- .../src/app/store/users/users.reducer.ts | 16 +++++++++-- 10 files changed, 132 insertions(+), 5 deletions(-) diff --git a/projects/organization-management/src/app/facades/organization-management.facade.ts b/projects/organization-management/src/app/facades/organization-management.facade.ts index 28974f50de1..8bc04392cd9 100644 --- a/projects/organization-management/src/app/facades/organization-management.facade.ts +++ b/projects/organization-management/src/app/facades/organization-management.facade.ts @@ -4,6 +4,7 @@ import { Store, select } from '@ngrx/store'; import { B2bUser } from '../models/b2b-user/b2b-user.model'; import { addUser, + deleteUser, getSelectedUser, getUsers, getUsersError, @@ -41,4 +42,8 @@ export class OrganizationManagementFacade { }) ); } + + deleteUser(login: string) { + this.store.dispatch(deleteUser({ login })); + } } diff --git a/projects/organization-management/src/app/pages/users/users-page.component.html b/projects/organization-management/src/app/pages/users/users-page.component.html index 2bfa304d8a2..72691dc61ba 100644 --- a/projects/organization-management/src/app/pages/users/users-page.component.html +++ b/projects/organization-management/src/app/pages/users/users-page.component.html @@ -15,7 +15,30 @@

{{ user.name }}
- +
+ + + + + +

{{ 'account.user.delete_user_dialog.are_you_sure_paragraph' | translate }}

+
+
diff --git a/projects/organization-management/src/app/pages/users/users-page.component.spec.ts b/projects/organization-management/src/app/pages/users/users-page.component.spec.ts index dd1f75f04f8..37b95eaacf2 100644 --- a/projects/organization-management/src/app/pages/users/users-page.component.spec.ts +++ b/projects/organization-management/src/app/pages/users/users-page.component.spec.ts @@ -1,5 +1,6 @@ import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { TranslateModule } from '@ngx-translate/core'; import { MockComponent } from 'ng-mocks'; import { of } from 'rxjs'; @@ -7,6 +8,7 @@ import { instance, mock, when } from 'ts-mockito'; import { ErrorMessageComponent } from 'ish-shared/components/common/error-message/error-message.component'; import { LoadingComponent } from 'ish-shared/components/common/loading/loading.component'; +import { ModalDialogComponent } from 'ish-shared/components/common/modal-dialog/modal-dialog.component'; import { OrganizationManagementFacade } from '../../facades/organization-management.facade'; import { B2bUser } from '../../models/b2b-user/b2b-user.model'; @@ -29,7 +31,13 @@ describe('Users Page Component', () => { TestBed.configureTestingModule({ imports: [RouterTestingModule, TranslateModule.forRoot()], - declarations: [MockComponent(ErrorMessageComponent), MockComponent(LoadingComponent), UsersPageComponent], + declarations: [ + MockComponent(ErrorMessageComponent), + MockComponent(FaIconComponent), + MockComponent(LoadingComponent), + MockComponent(ModalDialogComponent), + UsersPageComponent, + ], providers: [{ provide: OrganizationManagementFacade, useFactory: () => instance(organizationManagementFacade) }], }).compileComponents(); })); diff --git a/projects/organization-management/src/app/pages/users/users-page.component.ts b/projects/organization-management/src/app/pages/users/users-page.component.ts index 1a11bf398a5..cae1b2e624f 100644 --- a/projects/organization-management/src/app/pages/users/users-page.component.ts +++ b/projects/organization-management/src/app/pages/users/users-page.component.ts @@ -23,4 +23,8 @@ export class UsersPageComponent implements OnInit { this.error$ = this.organizationManagementFacade.usersError$; this.loading$ = this.organizationManagementFacade.usersLoading$; } + + deleteUser(user: B2bUser) { + this.organizationManagementFacade.deleteUser(user.login); + } } diff --git a/projects/organization-management/src/app/services/users/users.service.spec.ts b/projects/organization-management/src/app/services/users/users.service.spec.ts index b46fa09485c..3b58148ed60 100644 --- a/projects/organization-management/src/app/services/users/users.service.spec.ts +++ b/projects/organization-management/src/app/services/users/users.service.spec.ts @@ -17,6 +17,7 @@ describe('Users Service', () => { apiService = mock(ApiService); when(apiService.get(anything())).thenReturn(of(true)); when(apiService.resolveLinks()).thenReturn(() => of([])); + when(apiService.delete(anything())).thenReturn(of(true)); TestBed.configureTestingModule({ providers: [{ provide: ApiService, useFactory: () => instance(apiService) }], @@ -44,6 +45,18 @@ describe('Users Service', () => { usersService.getUser('pmiller@test.intershop.de').subscribe(() => { verify(apiService.get(anything())).once(); expect(capture(apiService.get).last()).toMatchInlineSnapshot(` + Array [ + "customers/-/users/pmiller@test.intershop.de", + ] + `); + done(); + }); + }); + + it('should call delete method of customer API when delete user', done => { + usersService.deleteUser('pmiller@test.intershop.de').subscribe(() => { + verify(apiService.delete(anything())).once(); + expect(capture(apiService.delete).last()).toMatchInlineSnapshot(` Array [ "customers/-/users/pmiller@test.intershop.de", ] diff --git a/projects/organization-management/src/app/services/users/users.service.ts b/projects/organization-management/src/app/services/users/users.service.ts index 9685d3590aa..6013cad4195 100644 --- a/projects/organization-management/src/app/services/users/users.service.ts +++ b/projects/organization-management/src/app/services/users/users.service.ts @@ -81,4 +81,17 @@ export class UsersService { }) .pipe(map(B2bUserMapper.fromData)); } + + /** + * Deletes the data of a b2b user. The current user is expected to have user management permission. + * @param login The login of the user. + * @returns The user. + */ + deleteUser(login: string) { + if (!login) { + return throwError('deleteUser() called without customerItemUserKey/login'); + } + + return this.apiService.delete(`customers/-/users/${login}`); + } } diff --git a/projects/organization-management/src/app/store/users/users.actions.ts b/projects/organization-management/src/app/store/users/users.actions.ts index 18468d4f700..1a62c17cfa4 100644 --- a/projects/organization-management/src/app/store/users/users.actions.ts +++ b/projects/organization-management/src/app/store/users/users.actions.ts @@ -27,3 +27,9 @@ export const updateUserFail = createAction('[Users API] Update User Fail', httpE export const updateUserSuccess = createAction('[Users API] Update User Success', payload<{ user: B2bUser }>()); export const resetUsers = createAction('[Users] Reset Users'); + +export const deleteUser = createAction('[Users API] Delete User', payload<{ login: string }>()); + +export const deleteUserFail = createAction('[Users API] Delete User Fail', httpError()); + +export const deleteUserSuccess = createAction('[Users API] Delete User Success', payload<{ login: string }>()); diff --git a/projects/organization-management/src/app/store/users/users.effects.spec.ts b/projects/organization-management/src/app/store/users/users.effects.spec.ts index 21b92ad5a94..e01972f2ef5 100644 --- a/projects/organization-management/src/app/store/users/users.effects.spec.ts +++ b/projects/organization-management/src/app/store/users/users.effects.spec.ts @@ -22,6 +22,7 @@ import { addUser, addUserFail, addUserSuccess, + deleteUser, loadUsers, loadUsersFail, updateUser, @@ -50,6 +51,8 @@ describe('Users Effects', () => { when(usersService.getUser(anything())).thenReturn(of(users[0])); when(usersService.addUser(anything())).thenReturn(of(users[0])); when(usersService.updateUser(anything())).thenReturn(of(users[0])); + when(usersService.getUsers()).thenReturn(of(users)); + when(usersService.deleteUser(anything())).thenReturn(of(true)); TestBed.configureTestingModule({ declarations: [DummyComponent], @@ -211,4 +214,29 @@ describe('Users Effects', () => { }); }); }); + + describe('deleteUser$', () => { + const login = 'pmiller@test.intershop.de'; + + it('should call the service for delete user', done => { + actions$ = of(deleteUser({ login })); + + effects.deleteUser$.subscribe(() => { + verify(usersService.deleteUser(anything())).once(); + done(); + }); + }); + + it('should delete user when triggered', done => { + actions$ = of(deleteUser({ login })); + + effects.deleteUser$.subscribe(action => { + expect(action).toMatchInlineSnapshot(` + [Users API] Delete User Success: + login: "pmiller@test.intershop.de" + `); + done(); + }); + }); + }); }); diff --git a/projects/organization-management/src/app/store/users/users.effects.ts b/projects/organization-management/src/app/store/users/users.effects.ts index 306c392d191..760e9855370 100644 --- a/projects/organization-management/src/app/store/users/users.effects.ts +++ b/projects/organization-management/src/app/store/users/users.effects.ts @@ -8,7 +8,7 @@ import { Customer } from 'ish-core/models/customer/customer.model'; import { displaySuccessMessage } from 'ish-core/store/core/messages'; import { selectRouteParam } from 'ish-core/store/core/router'; import { getLoggedInCustomer, logoutUser } from 'ish-core/store/customer/user'; -import { mapErrorToAction, mapToPayload, whenTruthy } from 'ish-core/utils/operators'; +import { mapErrorToAction, mapToPayload, mapToPayloadProperty, whenTruthy } from 'ish-core/utils/operators'; import { UsersService } from '../../services/users/users.service'; @@ -16,6 +16,9 @@ import { addUser, addUserFail, addUserSuccess, + deleteUser, + deleteUserFail, + deleteUserSuccess, loadUserFail, loadUserSuccess, loadUsers, @@ -110,6 +113,18 @@ export class UsersEffects { resetUsersAfterLogout$ = createEffect(() => this.actions$.pipe(ofType(logoutUser), mapTo(resetUsers()))); + deleteUser$ = createEffect(() => + this.actions$.pipe( + ofType(deleteUser), + mapToPayloadProperty('login'), + exhaustMap(login => + this.usersService + .deleteUser(login) + .pipe(map(() => deleteUserSuccess({ login }), mapErrorToAction(deleteUserFail))) + ) + ) + ); + private navigateToParent(): void { // find current ActivatedRoute by following first activated children let currentRoute = this.router.routerState.root; diff --git a/projects/organization-management/src/app/store/users/users.reducer.ts b/projects/organization-management/src/app/store/users/users.reducer.ts index 62c55da2e95..ceee309c46d 100644 --- a/projects/organization-management/src/app/store/users/users.reducer.ts +++ b/projects/organization-management/src/app/store/users/users.reducer.ts @@ -10,6 +10,9 @@ import { addUser, addUserFail, addUserSuccess, + deleteUser, + deleteUserFail, + deleteUserSuccess, loadUserFail, loadUserSuccess, loadUsers, @@ -37,8 +40,8 @@ const initialState: UsersState = usersAdapter.getInitialState({ export const usersReducer = createReducer( initialState, - setLoadingOn(loadUsers, addUser, updateUser), - setErrorOn(loadUsersFail, loadUserFail, addUserFail, updateUserFail), + setLoadingOn(loadUsers, addUser, updateUser, deleteUser), + setErrorOn(loadUsersFail, loadUserFail, addUserFail, updateUserFail, deleteUserFail), on(loadUsersSuccess, (state: UsersState, action) => { const { users } = action.payload; @@ -75,5 +78,14 @@ export const usersReducer = createReducer( error: undefined, }; }), + on(deleteUserSuccess, (state: UsersState, action) => { + const { login } = action.payload; + + return { + ...usersAdapter.removeOne(login, state), + loading: false, + error: undefined, + }; + }), on(resetUsers, () => initialState) );