Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix: TT-310 Reports does not work with groups fixed
  • Loading branch information
jcalarcon98 committed Aug 17, 2021
commit 84fe2c099b2deae840bd6ae3aff44d6a8d2b6175
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
<div class="table-responsive">
<table
*ngIf="users"
class="table table-sm table-bordered table-striped mb-0"
datatable
[dtTrigger]="dtTrigger"
[dtOptions]="dtOptions"
>
<table *ngIf="users" class="table table-sm table-bordered table-striped mb-0" datatable [dtTrigger]="dtTrigger"
[dtOptions]="dtOptions">
<thead class="thead-blue">
<tr class="d-flex flex-wrap">
<th class="col-4">User Email</th>
Expand All @@ -19,20 +14,14 @@
<td class="col-4 text-break">{{ user.email }}</td>
<td class="col-5 text-break">{{ user.name }}</td>
<td class="col-3 text-center">
<ui-switch
size="small"
(change)="switchGroup('time-tracker-admin', user)"
[checked]="user.groups.includes('time-tracker-admin')"
></ui-switch>
<ui-switch size="small" (change)="switchGroup('time-tracker-admin', user); updateRole(ROLES.admin, user);"
[checked]="user.groups.includes('time-tracker-admin')"></ui-switch>
admin
<ui-switch
size="small"
(change)="switchGroup('time-tracker-tester', user)"
[checked]="user.groups.includes('time-tracker-tester')"
></ui-switch>
<ui-switch size="small" (change)="switchGroup('time-tracker-tester', user)"
[checked]="user.groups.includes('time-tracker-tester')"></ui-switch>
test
</td>
</tr>
</tbody>
</table>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { NgxPaginationModule } from 'ngx-pagination';
import { UsersListComponent } from './users-list.component';
import {
UserActionTypes,
UserState,
LoadUsers,
AddUserToGroup,
RemoveUserFromGroup,
} from '../../store';
import { UserActionTypes, UserState, LoadUsers, AddUserToGroup, RemoveUserFromGroup } from '../../store';
import { ActionsSubject } from '@ngrx/store';
import { DataTablesModule } from 'angular-datatables';
import { GrantUserRole, RevokeUserRole } from '../../store/user.actions';

describe('UsersListComponent', () => {
let component: UsersListComponent;
Expand Down Expand Up @@ -39,10 +34,7 @@ describe('UsersListComponent', () => {
TestBed.configureTestingModule({
imports: [NgxPaginationModule, DataTablesModule],
declarations: [UsersListComponent],
providers: [
provideMockStore({ initialState: state }),
{ provide: ActionsSubject, useValue: actionSub },
],
providers: [provideMockStore({ initialState: state }), { provide: ActionsSubject, useValue: actionSub }],
}).compileComponents();
})
);
Expand Down Expand Up @@ -100,10 +92,7 @@ describe('UsersListComponent', () => {
});
});

const AddGroupTypes = [
{ groupName: 'time-tracker-admin' },
{ groupName: 'time-tracker-tester' }
];
const AddGroupTypes = [{ groupName: 'time-tracker-admin' }, { groupName: 'time-tracker-tester' }];

AddGroupTypes.map((param) => {
it(`When user switchGroup to ${param.groupName} and doesn't belong to any group, should add ${param.groupName} group to user`, () => {
Expand Down Expand Up @@ -179,6 +168,60 @@ describe('UsersListComponent', () => {
expect(component.dtElement.dtInstance.then).toHaveBeenCalled();
});

it('When the user does not have the role, this role must be added', () => {
const availableRoles = [
{
name: 'admin',
value: 'time-tracker-admin',
},
{
name: 'test',
value: 'time-tracker-tester',
},
];

spyOn(store, 'dispatch');

const user = {
id: 'no-matter-id',
name: 'no-matter-name',
email: 'no-matter-email',
roles: [],
};

availableRoles.forEach((role) => {
component.updateRole(role, user);
expect(store.dispatch).toHaveBeenCalledWith(new GrantUserRole(user.id, role.name));
});
});

it('When the user has the role, this role must be removed', () => {
const availableRoles = [
{
name: 'admin',
value: 'time-tracker-admin',
},
{
name: 'test',
value: 'time-tracker-tester',
},
];

spyOn(store, 'dispatch');

const user = {
id: 'no-matter-id',
name: 'no-matter-name',
email: 'no-matter-email',
roles: ['time-tracker-admin', 'time-tracker-tester'],
};

availableRoles.forEach((role) => {
component.updateRole(role, user);
expect(store.dispatch).toHaveBeenCalledWith(new RevokeUserRole(user.id, role.name));
});
});

afterEach(() => {
component.dtTrigger.unsubscribe();
component.loadUsersSubscription.unsubscribe();
Expand Down
26 changes: 16 additions & 10 deletions src/app/modules/users/components/users-list/users-list.component.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { GrantUserRole } from './../../store/user.actions';
import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ActionsSubject, select, Store, Action } from '@ngrx/store';
import { DataTableDirective } from 'angular-datatables';
import { Observable, Subject, Subscription } from 'rxjs';
import { delay, filter } from 'rxjs/operators';
import { ROLES } from 'src/environments/environment';
import { User } from '../../models/users';
import {
LoadUsers,
UserActionTypes,
AddUserToGroup,
RemoveUserFromGroup,
} from '../../store/user.actions';
import { LoadUsers, UserActionTypes, AddUserToGroup, RemoveUserFromGroup } from '../../store/user.actions';
import { getIsLoading } from '../../store/user.selectors';
import { RevokeUserRole } from '../../store/user.actions';

@Component({
selector: 'app-users-list',
Expand All @@ -28,10 +26,11 @@ export class UsersListComponent implements OnInit, OnDestroy, AfterViewInit {
dtOptions: any = {};
switchGroupsSubscription: Subscription;

constructor(
private store: Store<User>,
private actionsSubject$: ActionsSubject,
) {
public get ROLES() {
return ROLES;
}

constructor(private store: Store<User>, private actionsSubject$: ActionsSubject) {
this.isLoading$ = store.pipe(delay(0), select(getIsLoading));
}

Expand Down Expand Up @@ -77,6 +76,13 @@ export class UsersListComponent implements OnInit, OnDestroy, AfterViewInit {
);
}

updateRole(role: { name: string; value: string }, user: User) {
const userHasRole = user.roles.includes(role.value);
const action = userHasRole ? new RevokeUserRole(user.id, role.name) : new GrantUserRole(user.id, role.name);

this.store.dispatch(action);
}

filterUserGroup(): Observable<Action> {
return this.actionsSubject$.pipe(
filter(
Expand Down
42 changes: 42 additions & 0 deletions src/app/modules/users/store/user.actions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,46 @@ describe('UserActions', () => {

expect(action.type).toEqual(actions.UserActionTypes.REMOVE_USER_FROM_GROUP_FAIL);
});

it('GrantUserRole type is UserActionTypes.GRANT_USER_ROLE', () => {
const userId = 'no-matter-id';
const roleId = 'no-matter-role-id';
const action = new actions.GrantUserRole(userId, roleId);

expect(action.type).toEqual(actions.UserActionTypes.GRANT_USER_ROLE);
});

it('GrantUserRoleSuccess type is UserActionTypes.GRANT_USER_ROLE_SUCCESS', () => {
const user: User = { id: 'id', email: 'email', name: 'name' };
const action = new actions.GrantUserRoleSuccess(user);

expect(action.type).toEqual(actions.UserActionTypes.GRANT_USER_ROLE_SUCCESS);
});

it('GrantUserRoleFail type is UserActionTypes.GRANT_USER_ROLE_FAIL', () => {
const action = new actions.GrantUserRoleFail('error');

expect(action.type).toEqual(actions.UserActionTypes.GRANT_USER_ROLE_FAIL);
});

it('RevokeUserRole type is UserActionTypes.REVOKE_USER_ROLE', () => {
const userId = 'no-matter-id';
const roleId = 'no-matter-role-id';
const action = new actions.RevokeUserRole(userId, roleId);

expect(action.type).toEqual(actions.UserActionTypes.REVOKE_USER_ROLE);
});

it('RevokeUserRoleSuccess type is UserActionTypes.REVOKE_USER_ROLE_SUCCESS', () => {
const user: User = { id: 'id', email: 'email', name: 'name' };
const action = new actions.RevokeUserRoleSuccess(user);

expect(action.type).toEqual(actions.UserActionTypes.REVOKE_USER_ROLE_SUCCESS);
});

it('RevokeUserRoleFail type is UserActionTypes.REVOKE_USER_ROLE_FAIL', () => {
const action = new actions.RevokeUserRoleFail('error');

expect(action.type).toEqual(actions.UserActionTypes.REVOKE_USER_ROLE_FAIL);
});
});
62 changes: 52 additions & 10 deletions src/app/modules/users/store/user.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ export enum UserActionTypes {
REMOVE_USER_FROM_GROUP = '[User] REMOVE_USER_FROM_GROUP',
REMOVE_USER_FROM_GROUP_SUCCESS = '[User] REMOVE_USER_FROM_GROUP_SUCCESS',
REMOVE_USER_FROM_GROUP_FAIL = '[User] REMOVE_USER_FROM_GROUP_FAIL',
DEFAULT_USER = '[USER] DEFAULT_USER',
GRANT_USER_ROLE = '[User] GRANT_USER_ROLE',
GRANT_USER_ROLE_SUCCESS = '[User] GRANT_USER_ROLE_SUCCESS',
GRANT_USER_ROLE_FAIL = '[User] GRANT_USER_ROLE_FAIL',
REVOKE_USER_ROLE = '[User] REVOKE_USER_ROLE',
REVOKE_USER_ROLE_SUCCESS = '[User] REVOKE_USER_ROLE_SUCCESS',
REVOKE_USER_ROLE_FAIL = '[User] REVOKE_USER_ROLE_FAIL',
DEFAULT_USER = '[User] DEFAULT_USER',
}

export class LoadUsers implements Action {
Expand All @@ -20,42 +26,72 @@ export class LoadUsers implements Action {

export class LoadUsersSuccess implements Action {
readonly type = UserActionTypes.LOAD_USERS_SUCCESS;
constructor(readonly payload: User[]) { }
constructor(readonly payload: User[]) {}
}

export class LoadUsersFail implements Action {
public readonly type = UserActionTypes.LOAD_USERS_FAIL;
constructor(public error: string) { }
constructor(public error: string) {}
}

export class AddUserToGroup implements Action {
public readonly type = UserActionTypes.ADD_USER_TO_GROUP;
constructor(public userId: string, public groupName: string) { }
constructor(public userId: string, public groupName: string) {}
}

export class AddUserToGroupSuccess implements Action {
public readonly type = UserActionTypes.ADD_USER_TO_GROUP_SUCCESS;
constructor(readonly payload: User) { }
constructor(readonly payload: User) {}
}

export class AddUserToGroupFail implements Action {
public readonly type = UserActionTypes.ADD_USER_TO_GROUP_FAIL;
constructor(public error: string) { }
constructor(public error: string) {}
}

export class RemoveUserFromGroup implements Action {
public readonly type = UserActionTypes.REMOVE_USER_FROM_GROUP;
constructor(public userId: string, public groupName: string) { }
constructor(public userId: string, public groupName: string) {}
}

export class RemoveUserFromGroupSuccess implements Action {
public readonly type = UserActionTypes.REMOVE_USER_FROM_GROUP_SUCCESS;
constructor(readonly payload: User) { }
constructor(readonly payload: User) {}
}

export class RemoveUserFromGroupFail implements Action {
public readonly type = UserActionTypes.REMOVE_USER_FROM_GROUP_FAIL;
constructor(public error: string) { }
constructor(public error: string) {}
}

export class GrantUserRole implements Action {
public readonly type = UserActionTypes.GRANT_USER_ROLE;
constructor(public userId: string, public roleId: string) {}
}

export class GrantUserRoleSuccess implements Action {
public readonly type = UserActionTypes.GRANT_USER_ROLE_SUCCESS;
constructor(readonly payload: User) {}
}

export class GrantUserRoleFail implements Action {
public readonly type = UserActionTypes.GRANT_USER_ROLE_FAIL;
constructor(public error: string) {}
}

export class RevokeUserRole implements Action {
public readonly type = UserActionTypes.REVOKE_USER_ROLE;
constructor(public userId: string, public roleId: string) {}
}

export class RevokeUserRoleSuccess implements Action {
public readonly type = UserActionTypes.REVOKE_USER_ROLE_SUCCESS;
constructor(readonly payload: User) {}
}

export class RevokeUserRoleFail implements Action {
public readonly type = UserActionTypes.REVOKE_USER_ROLE_FAIL;
constructor(public error: string) {}
}

export class DefaultUser implements Action {
Expand All @@ -72,4 +108,10 @@ export type UserActions =
| AddUserToGroupFail
| RemoveUserFromGroup
| RemoveUserFromGroupSuccess
| RemoveUserFromGroupFail;
| RemoveUserFromGroupFail
| GrantUserRole
| GrantUserRoleSuccess
| GrantUserRoleFail
| RevokeUserRole
| RevokeUserRoleSuccess
| RevokeUserRoleFail;
Loading