Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Replace public implementation to display delegatee info with platform #3172

Merged
merged 13 commits into from
Aug 20, 2024
8 changes: 8 additions & 0 deletions src/app/core/mock-data/platform/v1/delegator.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import deepFreeze from 'deep-freeze-strict';
import { Delegator } from 'src/app/core/models/platform/delegator.model';

export const delegatorData: Delegator = deepFreeze({
user_id: '0x1234',
email: '[email protected]',
full_name: 'Vercetti',
});
47 changes: 47 additions & 0 deletions src/app/core/mock-data/platform/v1/platform-employee.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import deepFreeze from 'deep-freeze-strict';
import { DelegationType } from 'src/app/core/models/platform/delegation-type.enum';
import { PlatformApiResponse } from 'src/app/core/models/platform/platform-api-response.model';
import { PlatformEmployee } from 'src/app/core/models/platform/platform-employee.model';

export const platformEmployeeData: PlatformEmployee = deepFreeze({
code: null,
department: {
code: null,
display_name: 'Tech',
id: 'deptCjFrZcE0rH',
name: 'Tech',
sub_department: 'Tech',
},
department_id: 'deptCjFrZcE0rH',
id: 'ouirDZ7tTLEQ',
org_id: 'orNVthTo2Zyo',
user: {
email: '[email protected]',
full_name: 'Arjun',
id: 'usJZ9bgfNB5n',
},
user_id: 'usJZ9bgfNB5n',
delegatees: [
{
id: 100,
type: DelegationType.PERMANENT,
user_id: '0x1234',
email: '[email protected]',
full_name: 'Vercetti',
start_at: new Date(),
end_at: null,
},
],
});

export const platformEmployeeResponse: PlatformApiResponse<PlatformEmployee> = deepFreeze({
count: 1,
offset: 10,
data: platformEmployeeData,
});

export const platformEmployeeEmptyResponse: PlatformApiResponse<PlatformEmployee> = deepFreeze({
count: 1,
offset: 10,
data: null,
});
1 change: 1 addition & 0 deletions src/app/core/models/employee-params.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export interface EmployeeParams {
order: string;
limit: number;
us_email?: string;
us_id: string;
}
11 changes: 11 additions & 0 deletions src/app/core/models/platform/delegatee.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { DelegationType } from './delegation-type.enum';

export interface Delegatee {
id: number;
type: DelegationType;
user_id: string;
email: string;
full_name: string;
start_at: Date;
end_at: Date;
}
4 changes: 4 additions & 0 deletions src/app/core/models/platform/delegation-type.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum DelegationType {
SHORT_TERM,
PERMANENT,
}
5 changes: 5 additions & 0 deletions src/app/core/models/platform/delegator.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface Delegator {
user_id: string;
email: string;
full_name: string;
}
2 changes: 2 additions & 0 deletions src/app/core/models/platform/platform-employee.model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { User } from '../user.model';
import { Delegatee } from './delegatee.model';
import { Department } from './v1/department.model';

export interface PlatformEmployee {
Expand All @@ -9,4 +10,5 @@ export interface PlatformEmployee {
department?: Department;
department_id?: string;
org_id: string;
delegatees?: Delegatee[];
}
20 changes: 14 additions & 6 deletions src/app/core/services/org-user.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { DataTransformService } from './data-transform.service';
import { of } from 'rxjs';
import { OrgUserService } from './org-user.service';
import { TokenService } from './token.service';
import { delegatorData } from '../mock-data/platform/v1/delegator.data';
import { SpenderPlatformV1ApiService } from './spender-platform-v1-api.service';

describe('OrgUserService', () => {
let orgUserService: OrgUserService;
Expand All @@ -30,10 +32,12 @@ describe('OrgUserService', () => {
let authService: jasmine.SpyObj<AuthService>;
let dataTransformService: jasmine.SpyObj<DataTransformService>;
let apiV2Service: jasmine.SpyObj<ApiV2Service>;
let spenderPlatformV1ApiService: jasmine.SpyObj<SpenderPlatformV1ApiService>;

beforeEach(() => {
const apiServiceSpy = jasmine.createSpyObj('ApiService', ['get', 'post']);
const apiv2ServiceSpy = jasmine.createSpyObj('ApiV2Service', ['get']);
const spenderPlatformV1ApiServiceSpy = jasmine.createSpyObj('spenderPlatformV1ApiService', ['get']);
const jwtHelperServiceSpy = jasmine.createSpyObj('JwtHelperService', ['decodeToken']);
const tokenServiceSpy = jasmine.createSpyObj('TokenService', ['getAccessToken']);
const authServiceSpy = jasmine.createSpyObj('AuthService', ['newRefreshToken', 'refreshEou']);
Expand All @@ -50,6 +54,10 @@ describe('OrgUserService', () => {
provide: ApiV2Service,
useValue: apiv2ServiceSpy,
},
{
provide: SpenderPlatformV1ApiService,
useValue: spenderPlatformV1ApiServiceSpy,
},
{
provide: JwtHelperService,
useValue: jwtHelperServiceSpy,
Expand All @@ -71,6 +79,9 @@ describe('OrgUserService', () => {
orgUserService = TestBed.inject(OrgUserService);
apiService = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
apiV2Service = TestBed.inject(ApiV2Service) as jasmine.SpyObj<ApiV2Service>;
spenderPlatformV1ApiService = TestBed.inject(
SpenderPlatformV1ApiService
) as jasmine.SpyObj<SpenderPlatformV1ApiService>;
jwtHelperService = TestBed.inject(JwtHelperService) as jasmine.SpyObj<JwtHelperService>;
tokenService = TestBed.inject(TokenService) as jasmine.SpyObj<TokenService>;
authService = TestBed.inject(AuthService) as jasmine.SpyObj<AuthService>;
Expand Down Expand Up @@ -162,14 +173,11 @@ describe('OrgUserService', () => {
});

it('should be able to find delegated accounts', (done) => {
const eouList = [currentEouUnflatted];
apiService.get.and.returnValue(of(eouList));
eouList.map((delegatedAccount) =>
dataTransformService.unflatten.withArgs(delegatedAccount).and.returnValue(currentEouRes)
);
const delegatorList = { data: [delegatorData] };
spenderPlatformV1ApiService.get.and.returnValue(of(delegatorList));

orgUserService.findDelegatedAccounts().subscribe((res) => {
expect(res).toEqual([currentEouRes]);
expect(res).toEqual([delegatorData]);
done();
});
});
Expand Down
20 changes: 9 additions & 11 deletions src/app/core/services/org-user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
import { TokenService } from './token.service';
import { TrackingService } from './tracking.service';
import { AccessTokenData } from '../models/access-token-data.model';
import { Delegator } from '../models/platform/delegator.model';
import { SpenderPlatformV1ApiService } from './spender-platform-v1-api.service';
import { PlatformApiResponse } from '../models/platform/platform-api-response.model';

const orgUsersCacheBuster$ = new Subject<void>();

Expand All @@ -31,7 +34,8 @@
private authService: AuthService,
private dataTransformService: DataTransformService,
private trackingService: TrackingService,
private apiV2Service: ApiV2Service
private apiV2Service: ApiV2Service,
private spenderPlatformV1ApiService: SpenderPlatformV1ApiService
) {}

@Cacheable()
Expand All @@ -42,7 +46,7 @@
@Cacheable({
cacheBusterObserver: orgUsersCacheBuster$,
})
getEmployeesByParams(params: Partial<EmployeeParams>) {

Check failure on line 49 in src/app/core/services/org-user.service.ts

View workflow job for this annotation

GitHub Actions / Run linters

Missing return type on function
return this.apiV2Service.get<Partial<Employee>, {}>('/spender_employees', { params });
}

Expand All @@ -56,16 +60,10 @@
}

@Cacheable()
findDelegatedAccounts(): Observable<ExtendedOrgUser[]> {
return this.apiService.get<ExtendedOrgUser[]>('/eous/current/delegated_eous').pipe(
map((delegatedAccounts) => {
delegatedAccounts = delegatedAccounts.map((delegatedAccount) =>
this.dataTransformService.unflatten(delegatedAccount)
);

return delegatedAccounts;
})
);
findDelegatedAccounts(): Observable<Delegator[]> {
return this.spenderPlatformV1ApiService
.get<PlatformApiResponse<Delegator[]>>('/employees/delegators')
.pipe(map((response) => response.data));
}

postUser(user: User): Observable<User> {
Expand Down Expand Up @@ -106,7 +104,7 @@
if (eous) {
eousFiltered = eous.filter((eou) => status.indexOf(eou.ou.status) === -1);
}
return eousFiltered;

Check failure on line 107 in src/app/core/services/org-user.service.ts

View workflow job for this annotation

GitHub Actions / Run linters

Unsafe return of an `any[]` typed value
}

switchToDelegatee(): Observable<ExtendedOrgUser> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { SpenderService } from './spender.service';
import { commuteDetailsResponseData } from 'src/app/core/mock-data/commute-details-response.data';
import { of } from 'rxjs';
import { extendedOrgUserResponse } from 'src/app/core/test-data/tasks.service.spec.data';
import {
platformEmployeeData,
platformEmployeeResponse,
} from 'src/app/core/mock-data/platform/v1/platform-employee.data';
import { PlatformEmployee } from 'src/app/core/models/platform/platform-employee.model';

describe('EmployeesService', () => {
let service: EmployeesService;
Expand Down Expand Up @@ -50,4 +55,15 @@ describe('EmployeesService', () => {
done();
});
});

it('getByParams(): should get employees by params', () => {
spenderService.get.and.returnValue(of(platformEmployeeResponse));
const params: Partial<PlatformEmployee> = {
user_id: 'usJZ9bgfNB5n',
};

service.getByParams(params).subscribe((res) => {
expect(res).toBe(platformEmployeeResponse);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CommuteDetails } from 'src/app/core/models/platform/v1/commute-details.
import { CommuteDetailsResponse } from 'src/app/core/models/platform/commute-details-response.model';
import { PlatformApiResponse } from 'src/app/core/models/platform/platform-api-response.model';
import { ExtendedOrgUser } from 'src/app/core/models/extended-org-user.model';
import { PlatformEmployee } from 'src/app/core/models/platform/platform-employee.model';

@Injectable({
providedIn: 'root',
Expand All @@ -27,4 +28,8 @@ export class EmployeesService {
},
});
}

getByParams(params: Partial<PlatformEmployee>): Observable<PlatformApiResponse<PlatformEmployee>> {
return this.spenderService.get(`/employees`, { params });
}
}
10 changes: 4 additions & 6 deletions src/app/fyle/delegated-accounts/delegated-accounts.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,12 @@
<ion-grid>
<ion-row>
<ion-col size="2">
<div class="delegated--org-thumb">{{ acc?.us?.full_name | initials | uppercase }}</div>
<div class="delegated--org-thumb">{{ acc?.full_name | initials | uppercase }}</div>
</ion-col>
<ion-col size="10">
<div class="delegated--org-element-name">{{acc?.us?.full_name}}</div>
<div class="delegated--org-element-us-details">{{acc?.us?.email}}</div>
<div *ngIf="acc?.ou.org_id === currentOrg.id" class="delegated--org-element-us-details">
{{currentOrg.name}}
</div>
<div class="delegated--org-element-name">{{acc?.full_name}}</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if acc is undefined or null?
Are we handling the null cases as well?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are displaying switch accounts page only if its non empty, so it'll always have a value

<div class="delegated--org-element-us-details">{{acc?.email}}</div>
<div class="delegated--org-element-us-details">{{currentOrg.name}}</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why have we removed the condition here? @harshal015

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it'll be anyway visible for all delegators as the API will respond with delegators from same org.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can entirely remove the org name as well

</ion-col>
</ion-row>
</ion-grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { OrgUserService } from 'src/app/core/services/org-user.service';
import { OrgService } from 'src/app/core/services/org.service';
import { RecentLocalStorageItemsService } from 'src/app/core/services/recent-local-storage-items.service';
import { DelegatedAccountsPage } from './delegated-accounts.page';
import { delegatorData } from 'src/app/core/mock-data/platform/v1/delegator.data';

describe('DelegatedAccountsPage', () => {
let component: DelegatedAccountsPage;
Expand Down Expand Up @@ -132,21 +133,19 @@ describe('DelegatedAccountsPage', () => {
component.searchDelegatees = getElementRef(fixture, '.delegated--search-input');
const input = component.searchDelegatees.nativeElement as HTMLInputElement;
activatedRoute.snapshot.params.switchToOwn = null;
orgUserService.findDelegatedAccounts.and.returnValue(of([apiEouRes, eouRes2, eouRes3]));
orgUserService.findDelegatedAccounts.and.returnValue(of([delegatorData]));
orgService.getCurrentOrg.and.returnValue(of(orgData1[0]));
orgUserService.excludeByStatus.and.returnValue([eouRes2, eouRes3]);

component.ionViewWillEnter();
tick(500);

input.value = '[email protected]';
input.value = '[email protected]';
input.dispatchEvent(new Event('keyup'));
tick(500);

expect(component.delegatedAccList).toEqual([eouRes2, eouRes3]);
expect(component.delegatedAccList).toEqual([delegatorData]);
expect(orgUserService.findDelegatedAccounts).toHaveBeenCalledTimes(1);
expect(orgService.getCurrentOrg).toHaveBeenCalledTimes(1);
expect(orgUserService.excludeByStatus).toHaveBeenCalledWith([apiEouRes, eouRes2, eouRes3], 'DISABLED');
}));

it('should set delegatee acc list to empty array if no accounts are provided', fakeAsync(() => {
Expand All @@ -167,7 +166,6 @@ describe('DelegatedAccountsPage', () => {
expect(component.delegatedAccList).toEqual([]);
expect(orgUserService.findDelegatedAccounts).toHaveBeenCalledTimes(1);
expect(orgService.getCurrentOrg).toHaveBeenCalledTimes(1);
expect(orgUserService.excludeByStatus).toHaveBeenCalledWith([], 'DISABLED');
}));
});
});
31 changes: 20 additions & 11 deletions src/app/fyle/delegated-accounts/delegated-accounts.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { OrgUserService } from 'src/app/core/services/org-user.service';
import { OrgService } from 'src/app/core/services/org.service';
import { RecentLocalStorageItemsService } from 'src/app/core/services/recent-local-storage-items.service';
import { globalCacheBusterNotifier } from 'ts-cacheable';
import { User } from 'src/app/core/models/user.model';
import { Delegator } from 'src/app/core/models/platform/delegator.model';
import { DataTransformService } from 'src/app/core/services/data-transform.service';
import { EouApiResponse } from 'src/app/core/models/eou-api-response.model';

@Component({
selector: 'app-delegated-accounts',
Expand All @@ -27,16 +29,27 @@ export class DelegatedAccountsPage {
constructor(
private orgUserService: OrgUserService,
private orgService: OrgService,
private dataTransformService: DataTransformService,
private router: Router,
private loaderService: LoaderService,
private activatedRoute: ActivatedRoute,
private recentLocalStorageItemsService: RecentLocalStorageItemsService
) {}

switchToDelegatee(eou: ExtendedOrgUser): void {
switchToDelegatee(delegator: Delegator): void {
const params = {
us_id: `eq.${delegator.user_id}`,
};

from(this.loaderService.showLoader('Switching Account'))
.pipe(
concatMap(() => {
concatMap(() => this.orgUserService.getEmployeesByParams(params)),
concatMap((employee) =>
this.orgUserService
.getUserById(employee.data[0].ou_id)
.pipe(map((res) => this.dataTransformService.unflatten<ExtendedOrgUser, EouApiResponse>(res)))
),
concatMap((eou) => {
globalCacheBusterNotifier.next();
this.recentLocalStorageItemsService.clearRecentLocalStorageCache();
return this.orgUserService.switchToDelegator(eou.ou);
Expand Down Expand Up @@ -84,14 +97,10 @@ export class DelegatedAccountsPage {
distinctUntilChanged(),
switchMap((searchText) =>
delegatedAccList$.pipe(
map(({ delegatedAcc }) => this.orgUserService.excludeByStatus(delegatedAcc, 'DISABLED')),
map((delegatees: ExtendedOrgUser[]) =>
delegatees?.filter((delegatee: ExtendedOrgUser) =>
Object.values(delegatee.us).some(
(delegateeProp: User) =>
delegateeProp &&
delegateeProp.toString() &&
delegateeProp.toString().toLowerCase().includes(searchText.toLowerCase())
map(({ delegatedAcc }) =>
delegatedAcc.filter((delegator: Delegator) =>
Object.values(delegator).some((delegatorProp: string) =>
delegatorProp?.toString().toLowerCase().includes(searchText.toLowerCase())
)
)
)
Expand Down
Loading
Loading