fix(console): reduce initial load time (#8273)

This reduces the initial payload of the `listMyProjectOrgs` to get the
active org by setting the limit of the initial orgs to 100.

Partial of #8272

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Max Peintner
2024-07-22 16:18:26 +02:00
committed by GitHub
parent 0e99c8356a
commit 215dbf0cf6
5 changed files with 61 additions and 45 deletions

View File

@@ -26,6 +26,8 @@ export enum UserTarget {
EXTERNAL = 'external', EXTERNAL = 'external',
} }
const USER_LIMIT = 25;
@Component({ @Component({
selector: 'cnsl-search-user-autocomplete', selector: 'cnsl-search-user-autocomplete',
templateUrl: './search-user-autocomplete.component.html', templateUrl: './search-user-autocomplete.component.html',
@@ -67,9 +69,9 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
// feat-3916 show users as soon as I am in the input field of the user // feat-3916 show users as soon as I am in the input field of the user
const query = new SearchQuery(); const query = new SearchQuery();
const lnQuery = new LoginNameQuery(); const lnQuery = new LoginNameQuery();
lnQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE); lnQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_STARTS_WITH_IGNORE_CASE);
query.setLoginNameQuery(lnQuery); query.setLoginNameQuery(lnQuery);
this.userService.listUsers(10, 0, [query]).then((users) => { this.userService.listUsers(USER_LIMIT, 0, [query]).then((users) => {
this.filteredUsers = users.resultList; this.filteredUsers = users.resultList;
}); });
@@ -97,7 +99,7 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
query.setLoginNameQuery(lnQuery); query.setLoginNameQuery(lnQuery);
if (this.target === UserTarget.SELF) { if (this.target === UserTarget.SELF) {
return from(this.userService.listUsers(10, 0, [query])); return from(this.userService.listUsers(USER_LIMIT, 0, [query]));
} else { } else {
return of(); return of();
} }

View File

@@ -18,7 +18,7 @@ import { PageEvent, PaginatorComponent } from '../paginator/paginator.component'
import { UserGrantRoleDialogComponent } from '../user-grant-role-dialog/user-grant-role-dialog.component'; import { UserGrantRoleDialogComponent } from '../user-grant-role-dialog/user-grant-role-dialog.component';
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource'; import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource';
import { Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb'; import { Org, OrgIDQuery, OrgQuery, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
export enum UserGrantListSearchKey { export enum UserGrantListSearchKey {
DISPLAY_NAME, DISPLAY_NAME,
@@ -306,17 +306,15 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
} }
} }
public showUser(grant: UserGrant.AsObject) { public async showUser(grant: UserGrant.AsObject) {
const org: Org.AsObject = { const orgQuery = new OrgQuery();
id: grant.grantedOrgId, const orgIdQuery = new OrgIDQuery();
name: grant.grantedOrgName, orgIdQuery.setId(grant.grantedOrgId);
state: OrgState.ORG_STATE_ACTIVE, orgQuery.setIdQuery(orgIdQuery);
primaryDomain: grant.grantedOrgDomain,
};
// Check if user has permissions for that org before changing active org const orgs = (await this.authService.listMyProjectOrgs(1, 0, [orgQuery])).resultList;
if (this.myOrgs.find((org) => org.id === grant.grantedOrgId)) { if (orgs.length === 1) {
this.authService.setActiveOrg(org); this.authService.setActiveOrg(orgs[0]);
this.router.navigate(['/users', grant.userId]); this.router.navigate(['/users', grant.userId]);
} else { } else {
this.toast.showInfo('GRANTS.TOAST.CANTSHOWINFO', true); this.toast.showInfo('GRANTS.TOAST.CANTSHOWINFO', true);

View File

@@ -4,7 +4,7 @@
<cnsl-refresh-table <cnsl-refresh-table
[loading]="dataSource.loading$ | async" [loading]="dataSource.loading$ | async"
*ngIf="projectId" *ngIf="projectId"
(refreshed)="loadGrantsPage()" (refreshed)="refreshPage()"
[dataSize]="dataSource.totalResult" [dataSize]="dataSource.totalResult"
[selection]="selection" [selection]="selection"
[timestamp]="dataSource.viewTimestamp" [timestamp]="dataSource.viewTimestamp"
@@ -154,11 +154,11 @@
<cnsl-paginator <cnsl-paginator
class="paginator" class="paginator"
#paginator #paginator
[pageSize]="50" [pageSize]="INITIAL_PAGESIZE"
[timestamp]="dataSource.viewTimestamp" [timestamp]="dataSource.viewTimestamp"
[pageSizeOptions]="[25, 50, 100, 250]" [pageSizeOptions]="[25, 50, 100, 250]"
[length]="dataSource.totalResult" [length]="dataSource.totalResult"
(page)="loadGrantsPage($event.pageIndex, $event.pageSize)" (page)="loadGrantsPage($event)"
> >
</cnsl-paginator> </cnsl-paginator>
</cnsl-refresh-table> </cnsl-refresh-table>

View File

@@ -6,7 +6,7 @@ import { MatSelectChange } from '@angular/material/select';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { PaginatorComponent } from 'src/app/modules/paginator/paginator.component'; import { PageEvent, PaginatorComponent } from 'src/app/modules/paginator/paginator.component';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
import { GrantedProject, ProjectGrantState, Role } from 'src/app/proto/generated/zitadel/project_pb'; import { GrantedProject, ProjectGrantState, Role } from 'src/app/proto/generated/zitadel/project_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
@@ -14,8 +14,6 @@ import { ToastService } from 'src/app/services/toast.service';
import { ProjectGrantsDataSource } from './project-grants-datasource'; import { ProjectGrantsDataSource } from './project-grants-datasource';
const ROUTEPARAM = 'projectid';
@Component({ @Component({
selector: 'cnsl-project-grants', selector: 'cnsl-project-grants',
templateUrl: './project-grants.component.html', templateUrl: './project-grants.component.html',
@@ -28,7 +26,9 @@ const ROUTEPARAM = 'projectid';
]), ]),
], ],
}) })
export class ProjectGrantsComponent implements OnInit, AfterViewInit { export class ProjectGrantsComponent implements OnInit {
public INITIAL_PAGESIZE: number = 10;
@Input() public projectId: string = ''; @Input() public projectId: string = '';
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
@ViewChild(MatTable) public table!: MatTable<GrantedProject.AsObject>; @ViewChild(MatTable) public table!: MatTable<GrantedProject.AsObject>;
@@ -51,16 +51,12 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
} }
public ngOnInit(): void { public ngOnInit(): void {
this.dataSource.loadGrants(this.projectId, 0, 25, 'asc'); this.dataSource.loadGrants(this.projectId, 0, this.INITIAL_PAGESIZE);
this.getRoleOptions(this.projectId); this.getRoleOptions(this.projectId);
} }
public ngAfterViewInit(): void { public loadGrantsPage(event: PageEvent): void {
this.paginator.page.pipe(tap(() => this.loadGrantsPage())).subscribe(); this.dataSource.loadGrants(this.projectId, event.pageIndex, event.pageSize);
}
public loadGrantsPage(pageIndex?: number, pageSize?: number): void {
this.dataSource.loadGrants(this.projectId, pageIndex ?? this.paginator.pageIndex, pageSize ?? this.paginator.pageSize);
} }
public isAllSelected(): boolean { public isAllSelected(): boolean {
@@ -92,6 +88,11 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
}); });
} }
public refreshPage(): void {
this.selection.clear();
this.dataSource.loadGrants(this.projectId, this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize);
}
public deleteGrant(grant: GrantedProject.AsObject): void { public deleteGrant(grant: GrantedProject.AsObject): void {
const dialogRef = this.dialog.open(WarnDialogComponent, { const dialogRef = this.dialog.open(WarnDialogComponent, {
data: { data: {

View File

@@ -97,12 +97,14 @@ import {
import { ChangeQuery } from '../proto/generated/zitadel/change_pb'; import { ChangeQuery } from '../proto/generated/zitadel/change_pb';
import { MetadataQuery } from '../proto/generated/zitadel/metadata_pb'; import { MetadataQuery } from '../proto/generated/zitadel/metadata_pb';
import { ListQuery } from '../proto/generated/zitadel/object_pb'; import { ListQuery } from '../proto/generated/zitadel/object_pb';
import { Org, OrgFieldName, OrgQuery } from '../proto/generated/zitadel/org_pb'; import { Org, OrgFieldName, OrgIDQuery, OrgQuery } from '../proto/generated/zitadel/org_pb';
import { LabelPolicy, PrivacyPolicy } from '../proto/generated/zitadel/policy_pb'; import { LabelPolicy, PrivacyPolicy } from '../proto/generated/zitadel/policy_pb';
import { Gender, MembershipQuery, User, WebAuthNVerification } from '../proto/generated/zitadel/user_pb'; import { Gender, MembershipQuery, User, WebAuthNVerification } from '../proto/generated/zitadel/user_pb';
import { GrpcService } from './grpc.service'; import { GrpcService } from './grpc.service';
import { StorageKey, StorageLocation, StorageService } from './storage.service'; import { StorageKey, StorageLocation, StorageService } from './storage.service';
const ORG_LIMIT = 10;
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
@@ -249,36 +251,49 @@ export class GrpcAuthService {
this.setActiveOrg(find); this.setActiveOrg(find);
return Promise.resolve(find); return Promise.resolve(find);
} else { } else {
const orgs = (await this.listMyProjectOrgs(10, 0)).resultList; const orgQuery = new OrgQuery();
this.cachedOrgs.next(orgs); const orgIdQuery = new OrgIDQuery();
const toFind = orgs.find((tmp) => tmp.id === id); orgIdQuery.setId(id);
if (toFind) { orgQuery.setIdQuery(orgIdQuery);
this.setActiveOrg(toFind);
return Promise.resolve(toFind); const orgs = (await this.listMyProjectOrgs(ORG_LIMIT, 0, [orgQuery])).resultList;
if (orgs.length === 1) {
this.setActiveOrg(orgs[0]);
return Promise.resolve(orgs[0]);
} else { } else {
return Promise.reject(new Error('requested organization not found')); return Promise.reject(new Error('requested organization not found'));
} }
} }
} else { } else {
let orgs = this.cachedOrgs.getValue(); let orgs = this.cachedOrgs.getValue();
if (orgs.length === 0) {
orgs = (await this.listMyProjectOrgs()).resultList;
this.cachedOrgs.next(orgs);
}
const org = this.storage.getItem<Org.AsObject>(StorageKey.organization, StorageLocation.local); const org = this.storage.getItem<Org.AsObject>(StorageKey.organization, StorageLocation.local);
if (org && orgs.find((tmp) => tmp.id === org.id)) {
this.storage.setItem(StorageKey.organization, org, StorageLocation.session); if (org) {
this.setActiveOrg(org); const orgQuery = new OrgQuery();
return Promise.resolve(org); const orgIdQuery = new OrgIDQuery();
orgIdQuery.setId(org.id);
orgQuery.setIdQuery(orgIdQuery);
const specificOrg = (await this.listMyProjectOrgs(ORG_LIMIT, 0, [orgQuery])).resultList;
if (specificOrg.length === 1) {
this.setActiveOrg(specificOrg[0]);
return Promise.resolve(specificOrg[0]);
} else {
orgs = (await this.listMyProjectOrgs(ORG_LIMIT, 0)).resultList;
this.cachedOrgs.next(orgs);
}
} else {
orgs = (await this.listMyProjectOrgs(ORG_LIMIT, 0)).resultList;
this.cachedOrgs.next(orgs);
} }
if (orgs.length === 0) { if (orgs.length === 0) {
this._activeOrgChanged.next(undefined); this._activeOrgChanged.next(undefined);
return Promise.reject(new Error('No organizations found!')); return Promise.reject(new Error('No organizations found!'));
} }
const orgToSet = orgs.find((element) => element.id !== '0' && element.name !== '');
const orgToSet = orgs.find((element) => element.id !== '0' && element.name !== '');
if (orgToSet) { if (orgToSet) {
this.setActiveOrg(orgToSet); this.setActiveOrg(orgToSet);
return Promise.resolve(orgToSet); return Promise.resolve(orgToSet);
@@ -396,7 +411,7 @@ export class GrpcAuthService {
} }
public async revalidateOrgs() { public async revalidateOrgs() {
const orgs = (await this.listMyProjectOrgs()).resultList; const orgs = (await this.listMyProjectOrgs(ORG_LIMIT, 0)).resultList;
this.cachedOrgs.next(orgs); this.cachedOrgs.next(orgs);
} }