Skip to content

Commit

Permalink
feat(gui): server-side pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
rgmz authored and Richard Gomez committed Jul 3, 2024
1 parent 1cc3ddd commit a6baa51
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 66 deletions.
102 changes: 68 additions & 34 deletions roadrecon/frontend/src/app/appmain/aadobjects.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import {HttpClient, HttpParams} from '@angular/common/http';
import { environment } from '../../environments/environment';
import {
Router, Resolve,
RouterStateSnapshot,
ActivatedRouteSnapshot
} from '@angular/router';
ActivatedRouteSnapshot, Params
} from '@angular/router';
import { Observable, of, EMPTY } from 'rxjs';
import { mergeMap, take } from 'rxjs/operators';
import {SortDirection} from '@angular/material/sort';
Expand Down Expand Up @@ -245,8 +245,9 @@ export interface AuthorizationPolicy {
export interface PaginationParams {
page: number;
pageSize: number;
sortField: string;
sortDirection: SortDirection;
sortField?: string;
sortDirection?: SortDirection;
contains?: string;
}

@Injectable({
Expand All @@ -260,45 +261,48 @@ export class DatabaseService {

public getUsers(pagination?: PaginationParams): Observable<UsersItem[]> {
return this.http.get<UsersItem[]>(environment.apibase + 'users', {
params: {
page: pagination.page || this.defaultPage,
per_page: pagination.pageSize || this.defaultPageSize,
sort: pagination.sortField,
direction: pagination.sortDirection
}
params: this.toHttpParams(pagination)
});
}

public getUser(id): Observable<UsersItem> {
return this.http.get<UsersItem>(environment.apibase + 'users/'+ id);
}

public getDevices(): Observable<DevicesItem[]> {
return this.http.get<DevicesItem[]>(environment.apibase + 'devices');
public getDevices(pagination?: PaginationParams): Observable<DevicesItem[]> {
return this.http.get<DevicesItem[]>(environment.apibase + 'devices',
{ params: this.toHttpParams(pagination) }
);
}

public getDevice(id): Observable<DevicesItem> {
return this.http.get<DevicesItem>(environment.apibase + 'devices/'+ id);
}

public getGroups(): Observable<GroupsItem[]> {
return this.http.get<GroupsItem[]>(environment.apibase + 'groups');
public getGroups(pagination?: PaginationParams): Observable<GroupsItem[]> {
return this.http.get<GroupsItem[]>(environment.apibase + 'groups',
{ params: this.toHttpParams(pagination) }
);
}

public getGroup(id): Observable<GroupsItem> {
return this.http.get<GroupsItem>(environment.apibase + 'groups/'+ id);
}

public getAdministrativeUnits(): Observable<AdministrativeUnitsItem[]> {
return this.http.get<AdministrativeUnitsItem[]>(environment.apibase + 'administrativeunits');
public getAdministrativeUnits(pagination?: PaginationParams): Observable<AdministrativeUnitsItem[]> {
return this.http.get<AdministrativeUnitsItem[]>(environment.apibase + 'administrativeunits',
{ params: this.toHttpParams(pagination) }
);
}

public getAdministrativeUnit(id): Observable<AdministrativeUnitsItem> {
return this.http.get<AdministrativeUnitsItem>(environment.apibase + 'administrativeunits/'+ id);
}

public getServicePrincipals(): Observable<ServicePrincipalsItem[]> {
return this.http.get<ServicePrincipalsItem[]>(environment.apibase + 'serviceprincipals');
public getServicePrincipals(pagination?: PaginationParams): Observable<ServicePrincipalsItem[]> {
return this.http.get<ServicePrincipalsItem[]>(environment.apibase + 'serviceprincipals',
{ params: this.toHttpParams(pagination) }
);
}

public getServicePrincipal(id): Observable<ServicePrincipalsItem> {
Expand All @@ -309,20 +313,26 @@ export class DatabaseService {
return this.http.get<ServicePrincipalsItem>(environment.apibase + 'serviceprincipals-by-appid/'+ id);
}

public getApplications(): Observable<ApplicationsItem[]> {
return this.http.get<ApplicationsItem[]>(environment.apibase + 'applications');
public getApplications(pagination?: PaginationParams): Observable<ApplicationsItem[]> {
return this.http.get<ApplicationsItem[]>(environment.apibase + 'applications',
{ params: this.toHttpParams(pagination) }
);
}

public getApplication(id): Observable<ApplicationsItem> {
return this.http.get<ApplicationsItem>(environment.apibase + 'applications/'+ id);
}

public getDirectoryRoles(): Observable<DirectoryRolesItem[]> {
return this.http.get<DirectoryRolesItem[]>(environment.apibase + 'directoryroles');
public getDirectoryRoles(pagination?: PaginationParams): Observable<DirectoryRolesItem[]> {
return this.http.get<DirectoryRolesItem[]>(environment.apibase + 'directoryroles',
{ params: this.toHttpParams(pagination) }
);
}

public getRoleDefinitions(): Observable<RoleDefinitionsItem[]> {
return this.http.get<RoleDefinitionsItem[]>(environment.apibase + 'roledefinitions');
public getRoleDefinitions(pagination?: PaginationParams): Observable<RoleDefinitionsItem[]> {
return this.http.get<RoleDefinitionsItem[]>(environment.apibase + 'roledefinitions',
{ params: this.toHttpParams(pagination) }
);
}

public getTenantStats(): Observable<TenantStats> {
Expand All @@ -332,17 +342,21 @@ export class DatabaseService {
public getTenantDetail(): Observable<TenantDetail> {
return this.http.get<TenantDetail>(environment.apibase + 'tenantdetails');
}

public getDirectorySetting(): Observable<DirectorySetting> {
return this.http.get<DirectorySetting>(environment.apibase + 'directorysettings');
}

public getAuthorizationPolicies(): Observable<AuthorizationPolicy[]> {
return this.http.get<AuthorizationPolicy[]>(environment.apibase + 'authorizationpolicies');
public getAuthorizationPolicies(pagination?: PaginationParams): Observable<AuthorizationPolicy[]> {
return this.http.get<AuthorizationPolicy[]>(environment.apibase + 'authorizationpolicies',
{ params: this.toHttpParams(pagination) }
);
}

public getAppRoles(): Observable<AppRolesItem[]> {
return this.http.get<AppRolesItem[]>(environment.apibase + 'approles');
public getAppRoles(pagination?: PaginationParams): Observable<AppRolesItem[]> {
return this.http.get<AppRolesItem[]>(environment.apibase + 'approles',
{ params: this.toHttpParams(pagination) }
);
}

public getAppRolesByResource(spid): Observable<AppRolesItem[]> {
Expand All @@ -353,12 +367,32 @@ export class DatabaseService {
return this.http.get<AppRolesItem[]>(environment.apibase + 'approles_by_principal/' + pid);
}

public getMfa(): Observable<MfaItem[]> {
return this.http.get<MfaItem[]>(environment.apibase + 'mfa');
public getMfa(pagination?: PaginationParams): Observable<MfaItem[]> {
return this.http.get<MfaItem[]>(environment.apibase + 'mfa',
{ params: this.toHttpParams(pagination) }
);
}

public getOAuth2Permissions(pagination?: PaginationParams): Observable<OAuth2PermissionsItem[]> {
return this.http.get<OAuth2PermissionsItem[]>(environment.apibase + 'oauth2permissions',
{ params: this.toHttpParams(pagination) }
);
}

public getOAuth2Permissions(): Observable<OAuth2PermissionsItem[]> {
return this.http.get<OAuth2PermissionsItem[]>(environment.apibase + 'oauth2permissions');
toHttpParams(params?: PaginationParams): HttpParams {
let httpParams = new HttpParams();
httpParams = httpParams.set('page', params?.page || this.defaultPage);
httpParams = httpParams.set('per_page', params?.pageSize || this.defaultPageSize);
if (params?.sortField) {
httpParams = httpParams.set('sort', params.sortField);
}
if (params?.sortDirection) {
httpParams = httpParams.set('direction', params.sortDirection);
}
if (params?.contains) {
httpParams = httpParams.set('contains', params.contains);
}
return httpParams;
}
}

Expand Down
5 changes: 3 additions & 2 deletions roadrecon/frontend/src/app/appmain/appmain.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { DevicesdialogComponent } from './devices/devicesdialog/devicesdialog.co
import { JsonFormatDirective } from './json-format.directive';
import { ConfigComponent } from './config/config.component';
import { NgxWebstorageModule } from 'ngx-webstorage';
import { FormsModule } from '@angular/forms';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import { MfaComponent } from './mfa/mfa.component';
import { Oauth2permissionsComponent } from './oauth2permissions/oauth2permissions.component';

Expand Down Expand Up @@ -85,7 +85,8 @@ import { Oauth2permissionsComponent } from './oauth2permissions/oauth2permission
MatButtonModule,
MatTooltipModule,
MatSlideToggleModule,
NgxWebstorageModule.forRoot({'prefix':'RT'}),
NgxWebstorageModule.forRoot({'prefix': 'RT'}),
ReactiveFormsModule,
],
exports: [
UsersComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="mat-elevation-z8">
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
<input matInput [formControl]="filterControl" placeholder="Filter">
</mat-form-field>
<table mat-table class="full-width-table" matSort aria-label="Elements">
<ng-container matColumnDef="displayName">
Expand Down
31 changes: 25 additions & 6 deletions roadrecon/frontend/src/app/appmain/devices/devices.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTable, MatTableDataSource } from '@angular/material/table';
import { DatabaseService, DevicesItem } from '../aadobjects.service'
import {DatabaseService, DevicesItem, UsersItem} from '../aadobjects.service';
import {FormControl} from '@angular/forms';
// import
@Component({
selector: 'app-devices',
Expand All @@ -14,6 +15,7 @@ export class DevicesComponent implements AfterViewInit, OnInit {
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatTable) table: MatTable<DevicesItem>;
readonly filterControl = new FormControl('');
dataSource: MatTableDataSource<DevicesItem>;

constructor(private service: DatabaseService) { }
Expand All @@ -23,18 +25,35 @@ export class DevicesComponent implements AfterViewInit, OnInit {

ngOnInit() {
this.dataSource = new MatTableDataSource();
this.service.getDevices().subscribe((data: DevicesItem[]) => this.dataSource.data = data);
this.loadData();
}

ngAfterViewInit() {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
this.table.dataSource = this.dataSource;

this.filterControl.valueChanges.subscribe(() => this.loadData());
this.sort.sortChange.subscribe(() => {
this.paginator.pageIndex = 0; // Reset to first page on sort change
this.loadData();
});
this.paginator.page.subscribe(() => this.loadData());
}

applyFilter(filterValue: string) {
filterValue = filterValue.trim(); // Remove whitespace
filterValue = filterValue.toLowerCase(); // Datasource defaults to lowercase matches
this.dataSource.filter = filterValue;
loadData() {
let filterValue = this.filterControl.value;
if (filterValue) {
filterValue = filterValue.trim().toLowerCase();
}
this.service.getDevices(
{
page: this.paginator?.pageIndex,
pageSize: this.paginator?.pageSize,
sortField: this.sort?.active,
sortDirection: this.sort?.direction,
contains: filterValue,
}
).subscribe((data: DevicesItem[]) => this.dataSource.data = data);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="mat-elevation-z8">
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
<input matInput [formControl]="filterControl" placeholder="Filter">
</mat-form-field>
<table mat-table class="full-width-table" matSort aria-label="Elements">
<ng-container matColumnDef="displayName">
Expand Down
33 changes: 25 additions & 8 deletions roadrecon/frontend/src/app/appmain/groups/groups.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTable, MatTableDataSource } from '@angular/material/table';
// import { GroupsDataSource } from './groups-datasource';
import { DatabaseService, GroupsItem } from '../aadobjects.service'
import {DatabaseService, DevicesItem, GroupsItem} from '../aadobjects.service';
import {FormControl} from '@angular/forms';
// import
@Component({
selector: 'app-groups',
Expand All @@ -15,6 +16,7 @@ export class GroupsComponent implements AfterViewInit, OnInit {
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatTable) table: MatTable<GroupsItem>;
readonly filterControl = new FormControl('');
dataSource: MatTableDataSource<GroupsItem>;

constructor(private service: DatabaseService) { }
Expand All @@ -24,18 +26,33 @@ export class GroupsComponent implements AfterViewInit, OnInit {

ngOnInit() {
this.dataSource = new MatTableDataSource();
this.service.getGroups().subscribe((data: GroupsItem[]) => this.dataSource.data = data);
this.loadData();
}

ngAfterViewInit() {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
this.table.dataSource = this.dataSource;

this.filterControl.valueChanges.subscribe(() => this.loadData());
this.sort.sortChange.subscribe(() => {
this.paginator.pageIndex = 0; // Reset to first page on sort change
this.loadData();
});
this.paginator.page.subscribe(() => this.loadData());
}

applyFilter(filterValue: string) {
filterValue = filterValue.trim(); // Remove whitespace
filterValue = filterValue.toLowerCase(); // Datasource defaults to lowercase matches
this.dataSource.filter = filterValue;
loadData() {
let filterValue = this.filterControl.value;
if (filterValue) {
filterValue = filterValue.trim().toLowerCase();
}
this.service.getGroups(
{
page: this.paginator?.pageIndex,
pageSize: this.paginator?.pageSize,
sortField: this.sort?.active,
sortDirection: this.sort?.direction,
contains: filterValue,
}
).subscribe((data: GroupsItem[]) => this.dataSource.data = data);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="mat-elevation-z8">
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
<input matInput [formControl]="filterControl" placeholder="Filter">
</mat-form-field>
<table mat-table class="full-width-table" matSort aria-label="Elements">
<ng-container matColumnDef="displayName">
Expand Down
Loading

0 comments on commit a6baa51

Please sign in to comment.