Skip to content

Commit

Permalink
fix(guard): guards are now fixed and redirect to login and dashboard …
Browse files Browse the repository at this point in the history
…based on permission,fixed graph view,edge info can now be seen using right click on edge

* fix(guard): guards are now fixed and redirect to login and dashboard based on permission

* fix(login-form): added tests

* fix(data-analysis): fixed graph view

* fix(data-analysis): edge info can now be seen using right click on edge
  • Loading branch information
mojerf authored Sep 9, 2024
1 parent f92c259 commit 890fb5e
Show file tree
Hide file tree
Showing 15 changed files with 343 additions and 114 deletions.
4 changes: 2 additions & 2 deletions api-config/api-url.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const environment = {
apiUrl: 'https://localhost:44322',
// apiUrl: 'http://localhost:8085',
// apiUrl: 'https://localhost:44322',
apiUrl: 'http://localhost:8085',
};
22 changes: 8 additions & 14 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { MainPageComponent } from './user/components/dashboard/main-page/main-pa
import { ManageAccountComponent } from './user/components/dashboard/manage-account/manage-account.component';
import { DataAnalysisComponent } from './graph/components/data-analysis/data-analysis.component';
import { ManageUsersComponent } from './user/components/dashboard/manage-users/manage-users.component';
import { AppComponent } from './app.component';
import { AuthGuard } from './guards/auth/auth.guard';
import { PermissionGuard } from './guards/permissions/permission.guard';
import { AddGraphComponent } from './graph/components/add-graph/add-graph.component';
Expand All @@ -15,11 +14,14 @@ import { CategoryComponent } from './graph/components/category/category.componen
import { RecoverPassFormComponent } from './user/components/login/recover-pass-form/recover-pass-form.component';
import { LoginFormComponent } from './user/components/login/login-form/login-form.component';
import { ResetPasswordComponent } from './user/components/login/reset-password/reset-password.component';
import { LoginGuard } from './guards/auth/login.guard';

const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
{
path: '',
component: LoginComponent,
canActivate: [LoginGuard],
children: [
{
path: 'recover-password',
Expand All @@ -30,16 +32,9 @@ const routes: Routes = [
path: 'login',
component: LoginFormComponent,
title: 'StarData | Login',
// canActivate: [AuthGuard],
},
],
},
// {
// path: 'login',
// component: LoginComponent,
// title: 'StarData | Login',
// canActivate: [AuthGuard],
// },
{
path: 'dashboard',
component: DashboardComponent,
Expand All @@ -56,7 +51,7 @@ const routes: Routes = [
path: 'manage-users',
component: ManageUsersComponent,
title: 'StarData | Manage Users',
data: { permission: undefined },
data: { permission: 'Register' },
},
{
path: 'manage-account',
Expand All @@ -67,21 +62,25 @@ const routes: Routes = [
path: 'data-analysis',
component: DataAnalysisComponent,
title: 'StarData | Data Analysis',
data: { permission: 'GetNodesAsync' },
},
{
path: 'add-graph',
component: AddGraphComponent,
title: 'StarData | Add Graph',
data: { permission: 'UploadNodeFile' },
},
{
path: 'assign-file',
component: AssignFileComponent,
title: 'StarData | Assign File',
data: { permission: 'AccessFileToUser' },
},
{
path: 'manage-category',
component: CategoryComponent,
title: 'StarData | Manage Category',
data: { permission: 'GetCategories' },
},
],
},
Expand All @@ -90,11 +89,6 @@ const routes: Routes = [
component: ResetPasswordComponent,
title: 'StarData | Reset Password',
},
{
path: '',
component: AppComponent,
canActivate: [AuthGuard],
},
];

@NgModule({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@
></app-search-nodes>
</app-card>
<mat-menu #menu="matMenu" id="right-click-node-info">
<button mat-menu-item (click)="getInfo()">Show node information</button>
@if (isNode) {
<button mat-menu-item (click)="getNodeInfo()">Show node information</button>
<button mat-menu-item (click)="getGraph()">Show as graph</button>
} @else {
<button mat-menu-item (click)="getEdgeInfo()">Show edge information</button>
}
</mat-menu>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe('DataAnalysisComponent', () => {
mockLoadGraphService = jasmine.createSpyObj<LoadGraphService>([
'getAllNodes',
'getNodeInfo',
'getEdgeInfo',
'nodesData$',
'getGraph',
]);
Expand Down Expand Up @@ -79,7 +80,7 @@ describe('DataAnalysisComponent', () => {
fixture.detectChanges();

spyOn(document, 'getElementById').and.returnValue({
dataset: { nodeid: '123' },
dataset: { nodeid: '123', edgeid: '123' },
} as unknown as HTMLElement);
});

Expand All @@ -93,13 +94,24 @@ describe('DataAnalysisComponent', () => {
expect(component.isDarkMode).toBeTrue();
});

it('getInfo SHOULD show info WHEN get data successfully', () => {
it('getNodeInfo SHOULD show info WHEN get data successfully', () => {
// Arrange
mockLoadGraphService.getNodeInfo
.withArgs('123')
.and.returnValue(of({ name: 'mamad', id: 1 }));
// Act
component.getInfo();
component.getNodeInfo();
// Assert
expect(mockMatDialog.open).toHaveBeenCalled();
});

it('getEdgeInfo SHOULD show info WHEN get data successfully', () => {
// Arrange
mockLoadGraphService.getEdgeInfo
.withArgs('123')
.and.returnValue(of({ name: 'mamad', id: 1 }));
// Act
component.getEdgeInfo();
// Assert
expect(mockMatDialog.open).toHaveBeenCalled();
});
Expand Down
46 changes: 39 additions & 7 deletions src/app/graph/components/data-analysis/data-analysis.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class DataAnalysisComponent implements AfterViewInit {
isDarkMode = false;
nodeColor!: string;
selectedNodeColor!: string;
isNode!: boolean;

nodes = new DataSet<Node>([] as unknown as Node[]);
edges = new DataSet<Edge>([] as Edge[]);
Expand Down Expand Up @@ -92,6 +93,7 @@ export class DataAnalysisComponent implements AfterViewInit {
const edgeId = this.networkInstance.getEdgeAt(params.pointer.DOM);

if (nodeId !== undefined) {
this.isNode = true;
this.menuTrigger.nativeElement.style.left = params.event.clientX + 'px';
this.menuTrigger.nativeElement.style.top = params.event.clientY + 'px';
this.menuTrigger.nativeElement.style.position = 'fixed';
Expand All @@ -103,14 +105,20 @@ export class DataAnalysisComponent implements AfterViewInit {
) as HTMLElement;

rightClickNodeInfoElem.dataset['nodeid'] = nodeId.toString();

// Custom logic for node right-click
} else if (edgeId !== undefined) {
this.isNode = false;
console.log('Right-clicked edge:', edgeId);
// Custom logic for edge right-click
} else {
console.log('Right-clicked on empty space');
// Custom logic for right-click on empty space
this.menuTrigger.nativeElement.style.left = params.event.clientX + 'px';
this.menuTrigger.nativeElement.style.top = params.event.clientY + 'px';
this.menuTrigger.nativeElement.style.position = 'fixed';
this.matMenuTrigger.openMenu();

this.changeDetector.detectChanges();
const rightClickNodeInfoElem = document.getElementById(
'right-click-node-info'
) as HTMLElement;

rightClickNodeInfoElem.dataset['edgeid'] = edgeId.toString();
}
});

Expand Down Expand Up @@ -180,7 +188,7 @@ export class DataAnalysisComponent implements AfterViewInit {
console.log('edge click: ', edgeId);
}

getInfo() {
getNodeInfo() {
const account = (
document.getElementById('right-click-node-info') as HTMLElement
).dataset['nodeid'];
Expand All @@ -204,6 +212,30 @@ export class DataAnalysisComponent implements AfterViewInit {
});
}

getEdgeInfo() {
const account = (
document.getElementById('right-click-node-info') as HTMLElement
).dataset['edgeid'];

this.loadGraphService.getEdgeInfo(account!).subscribe({
next: (data) => {
this.dialog.open(InfoDialogComponent, {
width: '105rem',
data,
});
this.loadingService.setLoading(false);
},
error: (error) => {
this._snackBar.openFromComponent(DangerSuccessNotificationComponent, {
data: error.error.message,
panelClass: ['notification-class-danger'],
duration: 2000,
});
this.loadingService.setLoading(false);
},
});
}

showAsGraph(account: Account) {
this.nodes.add({ id: account.id, label: account.entityName });
}
Expand Down
20 changes: 7 additions & 13 deletions src/app/graph/components/data-analysis/graph-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,15 @@ export function getOptions() {

const svgDataUrl =
'data:image/svg+xml;charset=UTF-8,' +
encodeURIComponent(`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512l388.6 0c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304l-91.4 0z"
fill="${labelColor}"/>
</svg>
encodeURIComponent(`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="${labelColor}" >
<path d="M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Z"/>
</svg>
`);

return {
physics: false,
edges: {
width: 0.7,
smooth: { enabled: false, type: 'vertical', roundness: 0 },
arrows: {
to: {
Expand All @@ -40,13 +38,11 @@ export function getOptions() {
color: textColor,
strokeWidth: 0,
face: 'MyCustomFont',
size: 6,
},
},
nodes: {
shape: 'image',
image: svgDataUrl,
size: 8,
color: {
background: labelColor,
border: labelBorder,
Expand All @@ -59,7 +55,6 @@ export function getOptions() {
align: 'center',
color: textColor,
face: 'MyCustomFont',
size: 6,
},
},
} as Options;
Expand All @@ -69,10 +64,9 @@ export function getSvg(color: string, borderColor = color) {
return (
'data:image/svg+xml;charset=UTF-8,' +
encodeURIComponent(`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512l388.6 0c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304l-91.4 0z"
fill="${color}" stroke-width="2" stroke="${borderColor}"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="${color}" stroke-width="2" stroke="${borderColor}">
<path d="M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Z"/>
</svg>
`)
);
}
12 changes: 11 additions & 1 deletion src/app/graph/services/load-graph/load-graph.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,17 @@ export class LoadGraphService {
getNodeInfo(headerUniqueId: string) {
this.loadingService.setLoading(true);
return this.http.get<unknown>(
`${this.apiUrl}/nodes/${headerUniqueId}/attributes?id=${headerUniqueId}`,
`${this.apiUrl}/nodes/${headerUniqueId}/attributes`,
{
withCredentials: true,
}
);
}

getEdgeInfo(headerUniqueId: string) {
this.loadingService.setLoading(true);
return this.http.get<unknown>(
`${this.apiUrl}/edges/${headerUniqueId}/attributes?id=${headerUniqueId}`,
{
withCredentials: true,
}
Expand Down
26 changes: 4 additions & 22 deletions src/app/guards/auth/auth.guard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('AuthGuard', () => {
| UrlTree
| Observable<boolean | UrlTree>
| Promise<boolean | UrlTree>,
done: DoneFn,
done: DoneFn
) {
if (result instanceof Observable) {
result.subscribe((value) => {
Expand Down Expand Up @@ -78,25 +78,7 @@ describe('AuthGuard', () => {

authService.getPermissions.and.returnValue(of(mockPermissions));

const result = guard.canActivate(route);

handleResult(result, done);
});

it('should redirect to /dashboard if the user has permissions but tries to access another page', (done) => {
const mockPermissions = {
permission: ['viewDashboard'],
firstName: 'John',
lastName: 'Doe',
image: 'some-image-url',
};

route.url = [createMockUrlSegment('otherPage')];

authService.getPermissions.and.returnValue(of(mockPermissions));
router.parseUrl.and.returnValue('/dashboard' as unknown as UrlTree);

const result = guard.canActivate(route);
const result = guard.canActivate();

handleResult(result, done);
});
Expand All @@ -114,7 +96,7 @@ describe('AuthGuard', () => {
authService.getPermissions.and.returnValue(of(mockPermissions));
router.parseUrl.and.returnValue('/login' as unknown as UrlTree);

const result = guard.canActivate(route);
const result = guard.canActivate();

handleResult(result, done);
});
Expand All @@ -123,7 +105,7 @@ describe('AuthGuard', () => {
route.url = [createMockUrlSegment('dashboard')];
authService.getPermissions.and.returnValue(throwError('Error'));

const result = guard.canActivate(route);
const result = guard.canActivate();

if (result instanceof Observable) {
result.subscribe((value) => {
Expand Down
Loading

0 comments on commit 890fb5e

Please sign in to comment.