Skip to content

Commit

Permalink
url shortening shareable with invalid url redirection and filter rete…
Browse files Browse the repository at this point in the history
…ntion
  • Loading branch information
prabasak23 committed Feb 6, 2025
1 parent 4f917a9 commit 8ef12d5
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 347 deletions.
84 changes: 39 additions & 45 deletions UI/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { Router, RouteConfigLoadStart, RouteConfigLoadEnd, NavigationEnd, Activa
import { PrimeNGConfig } from 'primeng/api';
import { HelperService } from './services/helper.service';
import { Location } from '@angular/common';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
Expand Down Expand Up @@ -62,15 +64,10 @@ export class AppComponent implements OnInit {
/** Fetch projectId and sprintId from query param and save it to global object */
this.route.queryParams
.subscribe(params => {
console.log('params ', params)
if (!this.refreshCounter) {
console.log('params in ', params)
let stateFiltersParam = params['stateFilters'];
let kpiFiltersParam = params['kpiFilters'];

console.log('stateFiltersParam ', stateFiltersParam);
console.log('kpiFiltersParam ', kpiFiltersParam);

if (stateFiltersParam?.length) {
let selectedTab = decodeURIComponent(this.location.path());
selectedTab = selectedTab?.split('/')[2] ? selectedTab?.split('/')[2] : 'iteration';
Expand All @@ -79,9 +76,21 @@ export class AppComponent implements OnInit {
this.service.setSelectedBoard(this.selectedTab);

if (stateFiltersParam?.length <= 8 && kpiFiltersParam?.length <= 8) {
this.httpService.handleRestoreUrl(stateFiltersParam, kpiFiltersParam).subscribe((response: any) => {
console.log('response app compo', response)
try {
this.httpService.handleRestoreUrl(stateFiltersParam, kpiFiltersParam)
.pipe(
catchError((error) => {
this.router.navigate(['/dashboard/Error']); // Redirect to the error page
setTimeout(() => {
this.service.raiseError({
status: 900,
message: error.message || 'Invalid URL.'
});
});

return throwError(error); // Re-throw the error so it can be caught by a global error handler if needed
})
)
.subscribe((response: any) => {
if (response.success) {
const longKPIFiltersString = response.data['longKPIFiltersString'];
const longStateFiltersString = response.data['longStateFiltersString'];
Expand All @@ -96,30 +105,11 @@ export class AppComponent implements OnInit {
this.service.setKpiSubFilterObj(kpiFilterValFromUrl);
}

console.log('stateFiltersParam ', stateFiltersParam);
this.service.setBackupOfFilterSelectionState(JSON.parse(stateFiltersParam));
this.refreshCounter++;
} else {
this.router.navigate(['/dashboard/Error']); // Redirect to the error page
setTimeout(() => {
this.service.raiseError({
status: 900,
message: response.message || 'Invalid URL.'
});
});
}
} catch (error) {
this.router.navigate(['/dashboard/Error']); // Redirect to the error page
setTimeout(() => {
this.service.raiseError({
status: 900,
message: 'Invalid URL.'
});
})
}
});
});
} else {
console.log('not short url')
try {
// let selectedTab = this.location.path();
// selectedTab = selectedTab?.split('/')[2] ? selectedTab?.split('/')[2] : 'iteration';
Expand Down Expand Up @@ -191,21 +181,26 @@ export class AppComponent implements OnInit {
// let stateFiltersObj: Object = {};

if (stateFilters?.length <= 8) {
this.httpService.handleRestoreUrl(stateFilters, kpiFilters).subscribe((response: any) => {
if (response.success) {
const longStateFiltersString = response.data['longStateFiltersString'];
decodedStateFilters = atob(longStateFiltersString);
this.urlRedirection(decodedStateFilters, currentUserProjectAccess, url, ifSuperAdmin);
} else {
this.router.navigate(['/dashboard/Error']);
setTimeout(() => {
this.service.raiseError({
status: 900,
message: response.message || 'Invalid URL.',
});
}, 100);
}
});
this.httpService.handleRestoreUrl(stateFilters, kpiFilters)
.pipe(
catchError((error) => {
this.router.navigate(['/dashboard/Error']);
setTimeout(() => {
this.service.raiseError({
status: 900,
message: error.message || 'Invalid URL.',
});
}, 100);
return throwError(error); // Re-throw the error so it can be caught by a global error handler if needed
})
)
.subscribe((response: any) => {
if (response.success) {
const longStateFiltersString = response.data['longStateFiltersString'];
decodedStateFilters = atob(longStateFiltersString);
this.urlRedirection(decodedStateFilters, currentUserProjectAccess, url, ifSuperAdmin);
}
});
} else {
decodedStateFilters = atob(stateFilters);
this.urlRedirection(decodedStateFilters, currentUserProjectAccess, url, ifSuperAdmin);
Expand All @@ -230,7 +225,6 @@ export class AppComponent implements OnInit {

projectLevelSelected = stateFilterObj?.length && stateFilterObj[0]?.labelName?.toLowerCase() === 'project';


// Check if user has access to all project in stateFiltersObjLocal['primary_level']
const hasAllProjectAccess = stateFilterObj.every(filter =>
currentUserProjectAccess?.some(project => project.projectId === filter.basicProjectConfigId)
Expand All @@ -241,7 +235,7 @@ export class AppComponent implements OnInit {

if (projectLevelSelected) {
if (hasAccessToAll) {
this.router.navigate([JSON.parse(JSON.stringify(url))]);
this.router.navigate([url]);
} else {
this.router.navigate(['/dashboard/Error']);
this.service.raiseError({
Expand Down
112 changes: 6 additions & 106 deletions UI/src/app/authentication/login/login.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
import { of, throwError } from 'rxjs';
import { LoginComponent } from './login.component';
import { HttpService } from '../../services/http.service';
import { HelperService } from 'src/app/services/helper.service';
import { SharedService } from '../../services/shared.service';
import { GoogleAnalyticsService } from 'src/app/services/google-analytics.service';

Expand All @@ -32,6 +33,8 @@ describe('LoginComponent', () => {
let router: Router;
let httpService: HttpService;
let sharedService: SharedService;
let helperService: HelperService;
let ga: GoogleAnalyticsService;

const mockRouter = {
navigate: jasmine.createSpy('navigate'),
Expand All @@ -51,6 +54,8 @@ describe('LoginComponent', () => {
raiseError: jasmine.createSpy('raiseError'),
};

const mockHelperService = jasmine.createSpyObj('HelperService', ['urlShorteningRedirection']);

const mockGoogleAnalyticsService = {
setLoginMethod: jasmine.createSpy('setLoginMethod'),
};
Expand All @@ -64,6 +69,7 @@ describe('LoginComponent', () => {
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
{ provide: HttpService, useValue: mockHttpService },
{ provide: SharedService, useValue: mockSharedService },
{ provide: HelperService, useValue: mockHelperService },
{ provide: GoogleAnalyticsService, useValue: mockGoogleAnalyticsService },
],
}).compileComponents();
Expand Down Expand Up @@ -157,79 +163,6 @@ describe('LoginComponent', () => {
expect(mockRouter.navigate).toHaveBeenCalledWith(['./dashboard/Config/Profile']);
});

it('should redirect to dashboard on successful login if user has access', () => {
// Mock return values for sharedService methods
mockSharedService.getCurrentUserDetails.and.callFake((key) => {
const mockData = {
user_email: '[email protected]',
authorities: ['ROLE_SUPERADMIN'],
projectsAccess: [{}], // Non-empty projectsAccess
};
return mockData[key];
});

// Mock successful login response
const mockResponse = { status: 200, body: {} };
mockHttpService.login.and.returnValue(of(mockResponse));

// Call onSubmit
component.loginForm.controls['username'].setValue('testUser');
component.loginForm.controls['password'].setValue('testPass');
component.onSubmit();

// Verify navigation
expect(mockRouter.navigate).toHaveBeenCalledWith(['./dashboard/']);
});


it('should navigate to the provided URL if the user has access to all projects', () => {
const decodedStateFilters = JSON.stringify({
parent_level: { basicProjectConfigId: 'project1' },
primary_level: []
});
const stateFiltersObj = {};
const currentUserProjectAccess = [{ projectId: 'project1' }];
const url = 'http://example.com';

mockSharedService.getCurrentUserDetails.and.returnValue(['ROLE_USER']);

component.urlRedirection(decodedStateFilters, currentUserProjectAccess, url);

expect(router.navigate).toHaveBeenCalledWith([JSON.parse(JSON.stringify(url))]);
});

it('should navigate to the provided URL if the user is a superadmin', () => {
const decodedStateFilters = JSON.stringify({
parent_level: { basicProjectConfigId: 'project1' },
primary_level: []
});
const stateFiltersObj = {};
const currentUserProjectAccess = [];
const url = 'http://example.com';

mockSharedService.getCurrentUserDetails.and.returnValue(['ROLE_SUPERADMIN']);

component.urlRedirection(decodedStateFilters, currentUserProjectAccess, url);

expect(router.navigate).toHaveBeenCalledWith([JSON.parse(JSON.stringify(url))]);
});

it('should navigate to the error page if the user does not have access to all projects', () => {
const decodedStateFilters = JSON.stringify({
parent_level: { basicProjectConfigId: 'project1' },
primary_level: []
});
const stateFiltersObj = {};
const currentUserProjectAccess = [{ projectId: 'project2' }];
const url = 'http://example.com';

mockSharedService.getCurrentUserDetails.and.returnValue(['ROLE_USER']);

component.urlRedirection(decodedStateFilters, currentUserProjectAccess, url);

expect(router.navigate).toHaveBeenCalledWith(['/dashboard/Error']);
});

it('should handle 401 status code', () => {
const data = { status: 401, error: { message: 'Unauthorized' } };
component.performLogin(data, 'username', 'password');
Expand All @@ -251,39 +184,6 @@ describe('LoginComponent', () => {
expect(router.navigate).toHaveBeenCalledWith(['./dashboard/Config/Profile']);
});

it('should handle 200 status code with redirectToProfile() returning false and shared_link in local storage', () => {
spyOn(component, 'redirectToProfile').and.returnValue(false);
const data = { status: 200, body: {} };
localStorage.setItem('shared_link', 'https://example.com');
component.performLogin(data, 'username', 'password');
expect(router.navigate).toHaveBeenCalledWith(['/dashboard/Error']);
});

it('should handle 200 status code with redirectToProfile() returning false and no shared_link in local storage', () => {
spyOn(component, 'redirectToProfile').and.returnValue(false);
const data = { status: 200, body: {} };
localStorage.removeItem('shared_link');
component.performLogin(data, 'username', 'password');
expect(router.navigate).toHaveBeenCalledWith(['./dashboard/']);
});

it('should handle error for invalid URL', () => {
spyOn(component, 'redirectToProfile').and.returnValue(false);
const data = { status: 200, body: {} };
localStorage.setItem('shared_link', 'invalid-url');
component.performLogin(data, 'username', 'password');
expect(router.navigate).toHaveBeenCalledWith(['/dashboard/Error']);
});

it('should handle error for failed URL restoration', () => {
spyOn(component, 'redirectToProfile').and.returnValue(false);
const data = { status: 200, body: {} };
localStorage.setItem('shared_link', 'https://example.com');
mockHttpService.handleRestoreUrl.and.returnValue(throwError('Error restoring URL'));
component.performLogin(data, 'username', 'password');
expect(router.navigate).toHaveBeenCalledWith(['/dashboard/Error']);
});

// afterEach(() => {
// localStorage.removeItem('shared_link');
// });
Expand Down
Loading

0 comments on commit 8ef12d5

Please sign in to comment.