mirror of
https://github.com/zitadel/zitadel.git
synced 2025-04-27 15:20:51 +00:00
fix: load metadata using user service (#9429)
# Which Problems Are Solved - #9382 "When I log in and get to my user profile page, I get an empty error message at the top:" # How the Problems Are Solved load metadata using user service # Additional Changes - The roles observable returns an empty array instead of never emiting - Small refactorings in app.component.ts because at first I thought the errors stems from there. - Added withLatestFromSynchronousFix RXJS operator because withLatestFrom has confusing behavior when used in synchronous contexts. Why this operator is needed is described here: https://github.com/ReactiveX/rxjs/issues/7068 # Additional Context - Closes #9382
This commit is contained in:
parent
4df3b6492c
commit
b0f70626c8
@ -1,14 +1,14 @@
|
|||||||
import { BreakpointObserver } from '@angular/cdk/layout';
|
import { BreakpointObserver } from '@angular/cdk/layout';
|
||||||
import { OverlayContainer } from '@angular/cdk/overlay';
|
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||||
import { DOCUMENT, ViewportScroller } from '@angular/common';
|
import { DOCUMENT, ViewportScroller } from '@angular/common';
|
||||||
import { Component, HostBinding, HostListener, Inject, OnDestroy, ViewChild } from '@angular/core';
|
import { Component, DestroyRef, HostBinding, HostListener, Inject, OnDestroy, ViewChild } from '@angular/core';
|
||||||
import { MatIconRegistry } from '@angular/material/icon';
|
import { MatIconRegistry } from '@angular/material/icon';
|
||||||
import { MatDrawer } from '@angular/material/sidenav';
|
import { MatDrawer } from '@angular/material/sidenav';
|
||||||
import { DomSanitizer } from '@angular/platform-browser';
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
||||||
import { LangChangeEvent, TranslateService } from '@ngx-translate/core';
|
import { LangChangeEvent, TranslateService } from '@ngx-translate/core';
|
||||||
import { Observable, of, Subject } from 'rxjs';
|
import { Observable, of, Subject, switchMap } from 'rxjs';
|
||||||
import { filter, map, startWith, takeUntil } from 'rxjs/operators';
|
import { filter, map, startWith, takeUntil, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { accountCard, adminLineAnimation, navAnimations, routeAnimations, toolbarAnimation } from './animations';
|
import { accountCard, adminLineAnimation, navAnimations, routeAnimations, toolbarAnimation } from './animations';
|
||||||
import { Org } from './proto/generated/zitadel/org_pb';
|
import { Org } from './proto/generated/zitadel/org_pb';
|
||||||
@ -21,6 +21,7 @@ import { ThemeService } from './services/theme.service';
|
|||||||
import { UpdateService } from './services/update.service';
|
import { UpdateService } from './services/update.service';
|
||||||
import { fallbackLanguage, supportedLanguages, supportedLanguagesRegexp } from './utils/language';
|
import { fallbackLanguage, supportedLanguages, supportedLanguagesRegexp } from './utils/language';
|
||||||
import { PosthogService } from './services/posthog.service';
|
import { PosthogService } from './services/posthog.service';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'cnsl-root',
|
selector: 'cnsl-root',
|
||||||
@ -28,7 +29,7 @@ import { PosthogService } from './services/posthog.service';
|
|||||||
styleUrls: ['./app.component.scss'],
|
styleUrls: ['./app.component.scss'],
|
||||||
animations: [toolbarAnimation, ...navAnimations, accountCard, routeAnimations, adminLineAnimation],
|
animations: [toolbarAnimation, ...navAnimations, accountCard, routeAnimations, adminLineAnimation],
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnDestroy {
|
export class AppComponent {
|
||||||
@ViewChild('drawer') public drawer!: MatDrawer;
|
@ViewChild('drawer') public drawer!: MatDrawer;
|
||||||
public isHandset$: Observable<boolean> = this.breakpointObserver.observe('(max-width: 599px)').pipe(
|
public isHandset$: Observable<boolean> = this.breakpointObserver.observe('(max-width: 599px)').pipe(
|
||||||
map((result) => {
|
map((result) => {
|
||||||
@ -48,8 +49,6 @@ export class AppComponent implements OnDestroy {
|
|||||||
|
|
||||||
public showProjectSection: boolean = false;
|
public showProjectSection: boolean = false;
|
||||||
|
|
||||||
private destroy$: Subject<void> = new Subject();
|
|
||||||
|
|
||||||
public language: string = 'en';
|
public language: string = 'en';
|
||||||
public privacyPolicy!: PrivacyPolicy.AsObject;
|
public privacyPolicy!: PrivacyPolicy.AsObject;
|
||||||
constructor(
|
constructor(
|
||||||
@ -70,6 +69,7 @@ export class AppComponent implements OnDestroy {
|
|||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
@Inject(DOCUMENT) private document: Document,
|
@Inject(DOCUMENT) private document: Document,
|
||||||
private posthog: PosthogService,
|
private posthog: PosthogService,
|
||||||
|
private readonly destroyRef: DestroyRef,
|
||||||
) {
|
) {
|
||||||
console.log(
|
console.log(
|
||||||
'%cWait!',
|
'%cWait!',
|
||||||
@ -199,42 +199,43 @@ export class AppComponent implements OnDestroy {
|
|||||||
|
|
||||||
this.getProjectCount();
|
this.getProjectCount();
|
||||||
|
|
||||||
this.authService.activeOrgChanged.pipe(takeUntil(this.destroy$)).subscribe((org) => {
|
this.authService.activeOrgChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((org) => {
|
||||||
if (org) {
|
if (org) {
|
||||||
this.org = org;
|
this.org = org;
|
||||||
this.getProjectCount();
|
this.getProjectCount();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.activatedRoute.queryParams.pipe(filter((params) => !!params['org'])).subscribe((params) => {
|
this.activatedRoute.queryParamMap
|
||||||
const { org } = params;
|
.pipe(
|
||||||
this.authService.getActiveOrg(org);
|
map((params) => params.get('org')),
|
||||||
});
|
filter(Boolean),
|
||||||
|
takeUntilDestroyed(this.destroyRef),
|
||||||
|
)
|
||||||
|
.subscribe((org) => this.authService.getActiveOrg(org));
|
||||||
|
|
||||||
this.authenticationService.authenticationChanged.pipe(takeUntil(this.destroy$)).subscribe((authenticated) => {
|
this.authenticationService.authenticationChanged
|
||||||
if (authenticated) {
|
.pipe(
|
||||||
this.authService
|
filter(Boolean),
|
||||||
.getActiveOrg()
|
switchMap(() => this.authService.getActiveOrg()),
|
||||||
.then(async (org) => {
|
takeUntilDestroyed(this.destroyRef),
|
||||||
this.org = org;
|
)
|
||||||
// TODO add when console storage is implemented
|
.subscribe({
|
||||||
// this.startIntroWorkflow();
|
next: (org) => (this.org = org),
|
||||||
})
|
error: async (err) => {
|
||||||
.catch((error) => {
|
console.error(err);
|
||||||
console.error(error);
|
return this.router.navigate(['/users/me']);
|
||||||
this.router.navigate(['/users/me']);
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.isDarkTheme = this.themeService.isDarkTheme;
|
this.isDarkTheme = this.themeService.isDarkTheme;
|
||||||
this.isDarkTheme.pipe(takeUntil(this.destroy$)).subscribe((dark) => {
|
this.isDarkTheme.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((dark) => {
|
||||||
const theme = dark ? 'dark-theme' : 'light-theme';
|
const theme = dark ? 'dark-theme' : 'light-theme';
|
||||||
this.onSetTheme(theme);
|
this.onSetTheme(theme);
|
||||||
this.setFavicon(theme);
|
this.setFavicon(theme);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.translate.onLangChange.pipe(takeUntil(this.destroy$)).subscribe((language: LangChangeEvent) => {
|
this.translate.onLangChange.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((language: LangChangeEvent) => {
|
||||||
this.document.documentElement.lang = language.lang;
|
this.document.documentElement.lang = language.lang;
|
||||||
this.language = language.lang;
|
this.language = language.lang;
|
||||||
});
|
});
|
||||||
@ -254,11 +255,6 @@ export class AppComponent implements OnDestroy {
|
|||||||
// }, 1000);
|
// }, 1000);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
|
||||||
this.destroy$.next();
|
|
||||||
this.destroy$.complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public prepareRoute(outlet: RouterOutlet): boolean {
|
public prepareRoute(outlet: RouterOutlet): boolean {
|
||||||
return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation'];
|
return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation'];
|
||||||
}
|
}
|
||||||
@ -283,7 +279,7 @@ export class AppComponent implements OnDestroy {
|
|||||||
this.translate.addLangs(supportedLanguages);
|
this.translate.addLangs(supportedLanguages);
|
||||||
this.translate.setDefaultLang(fallbackLanguage);
|
this.translate.setDefaultLang(fallbackLanguage);
|
||||||
|
|
||||||
this.authService.user.pipe(filter(Boolean), takeUntil(this.destroy$)).subscribe((userprofile) => {
|
this.authService.user.pipe(filter(Boolean), takeUntilDestroyed(this.destroyRef)).subscribe((userprofile) => {
|
||||||
const cropped = navigator.language.split('-')[0] ?? fallbackLanguage;
|
const cropped = navigator.language.split('-')[0] ?? fallbackLanguage;
|
||||||
const fallbackLang = cropped.match(supportedLanguagesRegexp) ? cropped : fallbackLanguage;
|
const fallbackLang = cropped.match(supportedLanguagesRegexp) ? cropped : fallbackLanguage;
|
||||||
|
|
||||||
@ -306,7 +302,7 @@ export class AppComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setFavicon(theme: string): void {
|
private setFavicon(theme: string): void {
|
||||||
this.authService.labelpolicy$.pipe(startWith(undefined), takeUntil(this.destroy$)).subscribe((lP) => {
|
this.authService.labelpolicy$.pipe(startWith(undefined), takeUntilDestroyed(this.destroyRef)).subscribe((lP) => {
|
||||||
if (theme === 'dark-theme' && lP?.iconUrlDark) {
|
if (theme === 'dark-theme' && lP?.iconUrlDark) {
|
||||||
// Check if asset url is stable, maybe it was deleted but still wasn't applied
|
// Check if asset url is stable, maybe it was deleted but still wasn't applied
|
||||||
fetch(lP.iconUrlDark).then((response) => {
|
fetch(lP.iconUrlDark).then((response) => {
|
||||||
|
@ -5,19 +5,7 @@ import { MatDialog } from '@angular/material/dialog';
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
import {
|
import { defer, EMPTY, fromEvent, mergeWith, Observable, of, shareReplay, Subject, switchMap, take } from 'rxjs';
|
||||||
combineLatestWith,
|
|
||||||
defer,
|
|
||||||
EMPTY,
|
|
||||||
fromEvent,
|
|
||||||
mergeWith,
|
|
||||||
Observable,
|
|
||||||
of,
|
|
||||||
shareReplay,
|
|
||||||
Subject,
|
|
||||||
switchMap,
|
|
||||||
take,
|
|
||||||
} from 'rxjs';
|
|
||||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||||
import { phoneValidator, requiredValidator } from 'src/app/modules/form-field/validators/validators';
|
import { phoneValidator, requiredValidator } from 'src/app/modules/form-field/validators/validators';
|
||||||
import { InfoDialogComponent } from 'src/app/modules/info-dialog/info-dialog.component';
|
import { InfoDialogComponent } from 'src/app/modules/info-dialog/info-dialog.component';
|
||||||
@ -37,7 +25,7 @@ import { formatPhone } from 'src/app/utils/formatPhone';
|
|||||||
import { EditDialogComponent, EditDialogData, EditDialogResult, EditDialogType } from './edit-dialog/edit-dialog.component';
|
import { EditDialogComponent, EditDialogData, EditDialogResult, EditDialogType } from './edit-dialog/edit-dialog.component';
|
||||||
import { LanguagesService } from 'src/app/services/languages.service';
|
import { LanguagesService } from 'src/app/services/languages.service';
|
||||||
import { Gender, HumanProfile, HumanUser, User, UserState } from '@zitadel/proto/zitadel/user/v2/user_pb';
|
import { Gender, HumanProfile, HumanUser, User, UserState } from '@zitadel/proto/zitadel/user/v2/user_pb';
|
||||||
import { catchError, filter, map, startWith, tap, withLatestFrom } from 'rxjs/operators';
|
import { catchError, filter, map, startWith, withLatestFrom } from 'rxjs/operators';
|
||||||
import { pairwiseStartWith } from 'src/app/utils/pairwiseStartWith';
|
import { pairwiseStartWith } from 'src/app/utils/pairwiseStartWith';
|
||||||
import { NewAuthService } from 'src/app/services/new-auth.service';
|
import { NewAuthService } from 'src/app/services/new-auth.service';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
@ -47,12 +35,12 @@ import { UserService } from 'src/app/services/user.service';
|
|||||||
import { LoginPolicy } from '@zitadel/proto/zitadel/policy_pb';
|
import { LoginPolicy } from '@zitadel/proto/zitadel/policy_pb';
|
||||||
import { query } from '@angular/animations';
|
import { query } from '@angular/animations';
|
||||||
|
|
||||||
type UserQuery = { state: 'success'; value: User } | { state: 'error'; value: string } | { state: 'loading'; value?: User };
|
type UserQuery = { state: 'success'; value: User } | { state: 'error'; error: any } | { state: 'loading'; value?: User };
|
||||||
|
|
||||||
type MetadataQuery =
|
type MetadataQuery =
|
||||||
| { state: 'success'; value: Metadata[] }
|
| { state: 'success'; value: Metadata[] }
|
||||||
| { state: 'loading'; value: Metadata[] }
|
| { state: 'loading'; value: Metadata[] }
|
||||||
| { state: 'error'; value: string };
|
| { state: 'error'; error: any };
|
||||||
|
|
||||||
type UserWithHumanType = Omit<User, 'type'> & { type: { case: 'human'; value: HumanUser } };
|
type UserWithHumanType = Omit<User, 'type'> & { type: { case: 'human'; value: HumanUser } };
|
||||||
|
|
||||||
@ -92,9 +80,9 @@ export class AuthUserDetailComponent implements OnInit {
|
|||||||
protected readonly userName$: Observable<string>;
|
protected readonly userName$: Observable<string>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
public grpcAuthService: GrpcAuthService,
|
protected grpcAuthService: GrpcAuthService,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
private auth: AuthenticationService,
|
private auth: AuthenticationService,
|
||||||
private breadcrumbService: BreadcrumbService,
|
private breadcrumbService: BreadcrumbService,
|
||||||
@ -111,7 +99,7 @@ export class AuthUserDetailComponent implements OnInit {
|
|||||||
this.user$ = this.getUser$().pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
this.user$ = this.getUser$().pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
||||||
this.userName$ = this.getUserName(this.user$);
|
this.userName$ = this.getUserName(this.user$);
|
||||||
this.savedLanguage$ = this.getSavedLanguage$(this.user$);
|
this.savedLanguage$ = this.getSavedLanguage$(this.user$);
|
||||||
this.metadata$ = this.getMetadata$(this.user$).pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
this.metadata$ = this.getMetadata$().pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
||||||
|
|
||||||
this.loginPolicy$ = defer(() => this.newMgmtService.getLoginPolicy()).pipe(
|
this.loginPolicy$ = defer(() => this.newMgmtService.getLoginPolicy()).pipe(
|
||||||
catchError(() => EMPTY),
|
catchError(() => EMPTY),
|
||||||
@ -164,7 +152,7 @@ export class AuthUserDetailComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
this.user$.pipe(mergeWith(this.metadata$), takeUntilDestroyed(this.destroyRef)).subscribe((query) => {
|
this.user$.pipe(mergeWith(this.metadata$), takeUntilDestroyed(this.destroyRef)).subscribe((query) => {
|
||||||
if (query.state == 'error') {
|
if (query.state == 'error') {
|
||||||
this.toast.showError(query.value);
|
this.toast.showError(query.error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -206,24 +194,15 @@ export class AuthUserDetailComponent implements OnInit {
|
|||||||
private getMyUser(): Observable<UserQuery> {
|
private getMyUser(): Observable<UserQuery> {
|
||||||
return defer(() => this.userService.getMyUser()).pipe(
|
return defer(() => this.userService.getMyUser()).pipe(
|
||||||
map((user) => ({ state: 'success' as const, value: user })),
|
map((user) => ({ state: 'success' as const, value: user })),
|
||||||
catchError((error) => of({ state: 'error', value: error.message ?? '' } as const)),
|
catchError((error) => of({ state: 'error', error } as const)),
|
||||||
startWith({ state: 'loading' } as const),
|
startWith({ state: 'loading' } as const),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMetadata$(user$: Observable<UserQuery>): Observable<MetadataQuery> {
|
getMetadata$(): Observable<MetadataQuery> {
|
||||||
return this.refreshMetadata$.pipe(
|
return this.refreshMetadata$.pipe(
|
||||||
startWith(true),
|
startWith(true),
|
||||||
combineLatestWith(user$),
|
switchMap(() => this.getMetadata()),
|
||||||
switchMap(([_, user]) => {
|
|
||||||
if (!(user.state === 'success' || user.state === 'loading')) {
|
|
||||||
return EMPTY;
|
|
||||||
}
|
|
||||||
if (!user.value) {
|
|
||||||
return EMPTY;
|
|
||||||
}
|
|
||||||
return this.getMetadataById(user.value.userId);
|
|
||||||
}),
|
|
||||||
pairwiseStartWith(undefined),
|
pairwiseStartWith(undefined),
|
||||||
map(([prev, curr]) => {
|
map(([prev, curr]) => {
|
||||||
if (prev?.state === 'success' && curr.state === 'loading') {
|
if (prev?.state === 'success' && curr.state === 'loading') {
|
||||||
@ -234,11 +213,11 @@ export class AuthUserDetailComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMetadataById(userId: string): Observable<MetadataQuery> {
|
private getMetadata(): Observable<MetadataQuery> {
|
||||||
return defer(() => this.newMgmtService.listUserMetadata(userId)).pipe(
|
return defer(() => this.newAuthService.listMyMetadata()).pipe(
|
||||||
map((metadata) => ({ state: 'success', value: metadata.result }) as const),
|
map((metadata) => ({ state: 'success', value: metadata.result }) as const),
|
||||||
startWith({ state: 'loading', value: [] as Metadata[] } as const),
|
startWith({ state: 'loading', value: [] as Metadata[] } as const),
|
||||||
catchError((err) => of({ state: 'error', value: err.message ?? '' } as const)),
|
catchError((error) => of({ state: 'error', error } as const)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,6 +445,7 @@ export class UserDetailComponent implements OnInit {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const dialogRef = this.dialog.open<WarnDialogComponent, typeof data, boolean>(WarnDialogComponent, {
|
const dialogRef = this.dialog.open<WarnDialogComponent, typeof data, boolean>(WarnDialogComponent, {
|
||||||
|
data,
|
||||||
width: '400px',
|
width: '400px',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,21 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { SortDirection } from '@angular/material/sort';
|
import { SortDirection } from '@angular/material/sort';
|
||||||
import { OAuthService } from 'angular-oauth2-oidc';
|
import { OAuthService } from 'angular-oauth2-oidc';
|
||||||
import {
|
import { BehaviorSubject, combineLatestWith, EMPTY, mergeWith, NEVER, Observable, of, shareReplay, Subject } from 'rxjs';
|
||||||
BehaviorSubject,
|
|
||||||
combineLatestWith,
|
|
||||||
defer,
|
|
||||||
distinctUntilKeyChanged,
|
|
||||||
EMPTY,
|
|
||||||
forkJoin,
|
|
||||||
mergeWith,
|
|
||||||
NEVER,
|
|
||||||
Observable,
|
|
||||||
of,
|
|
||||||
shareReplay,
|
|
||||||
Subject,
|
|
||||||
TimeoutError,
|
|
||||||
} from 'rxjs';
|
|
||||||
import { catchError, distinctUntilChanged, filter, finalize, map, startWith, switchMap, tap, timeout } from 'rxjs/operators';
|
import { catchError, distinctUntilChanged, filter, finalize, map, startWith, switchMap, tap, timeout } from 'rxjs/operators';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -186,7 +172,6 @@ export class GrpcAuthService {
|
|||||||
.then((resp) => resp.resultList)
|
.then((resp) => resp.resultList)
|
||||||
.catch(() => <string[]>[]),
|
.catch(() => <string[]>[]),
|
||||||
),
|
),
|
||||||
filter((roles) => !!roles.length),
|
|
||||||
distinctUntilChanged((a, b) => {
|
distinctUntilChanged((a, b) => {
|
||||||
return JSON.stringify(a.sort()) === JSON.stringify(b.sort());
|
return JSON.stringify(a.sort()) === JSON.stringify(b.sort());
|
||||||
}),
|
}),
|
||||||
@ -302,7 +287,6 @@ export class GrpcAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this.zitadelPermissions.pipe(
|
return this.zitadelPermissions.pipe(
|
||||||
filter((permissions) => !!permissions.length),
|
|
||||||
map((permissions) => this.hasRoles(permissions, roles, requiresAll)),
|
map((permissions) => this.hasRoles(permissions, roles, requiresAll)),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
);
|
);
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { GrpcService } from './grpc.service';
|
import { GrpcService } from './grpc.service';
|
||||||
import { create } from '@bufbuild/protobuf';
|
|
||||||
import {
|
import {
|
||||||
AddMyAuthFactorOTPSMSRequestSchema,
|
|
||||||
AddMyAuthFactorOTPSMSResponse,
|
AddMyAuthFactorOTPSMSResponse,
|
||||||
GetMyUserRequestSchema,
|
|
||||||
GetMyUserResponse,
|
GetMyUserResponse,
|
||||||
VerifyMyPhoneRequestSchema,
|
ListMyMetadataResponse,
|
||||||
VerifyMyPhoneResponse,
|
VerifyMyPhoneResponse,
|
||||||
} from '@zitadel/proto/zitadel/auth_pb';
|
} from '@zitadel/proto/zitadel/auth_pb';
|
||||||
|
|
||||||
@ -17,14 +14,18 @@ export class NewAuthService {
|
|||||||
constructor(private readonly grpcService: GrpcService) {}
|
constructor(private readonly grpcService: GrpcService) {}
|
||||||
|
|
||||||
public getMyUser(): Promise<GetMyUserResponse> {
|
public getMyUser(): Promise<GetMyUserResponse> {
|
||||||
return this.grpcService.authNew.getMyUser(create(GetMyUserRequestSchema));
|
return this.grpcService.authNew.getMyUser({});
|
||||||
}
|
}
|
||||||
|
|
||||||
public verifyMyPhone(code: string): Promise<VerifyMyPhoneResponse> {
|
public verifyMyPhone(code: string): Promise<VerifyMyPhoneResponse> {
|
||||||
return this.grpcService.authNew.verifyMyPhone(create(VerifyMyPhoneRequestSchema, { code }));
|
return this.grpcService.authNew.verifyMyPhone({});
|
||||||
}
|
}
|
||||||
|
|
||||||
public addMyAuthFactorOTPSMS(): Promise<AddMyAuthFactorOTPSMSResponse> {
|
public addMyAuthFactorOTPSMS(): Promise<AddMyAuthFactorOTPSMSResponse> {
|
||||||
return this.grpcService.authNew.addMyAuthFactorOTPSMS(create(AddMyAuthFactorOTPSMSRequestSchema));
|
return this.grpcService.authNew.addMyAuthFactorOTPSMS({});
|
||||||
|
}
|
||||||
|
|
||||||
|
public listMyMetadata(): Promise<ListMyMetadataResponse> {
|
||||||
|
return this.grpcService.authNew.listMyMetadata({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
console/src/app/utils/withLatestFromSynchronousFix.ts
Normal file
19
console/src/app/utils/withLatestFromSynchronousFix.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { combineLatestWith, distinctUntilChanged, Observable, ObservableInput, ObservableInputTuple } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
// withLatestFrom does not work in this case, so we use
|
||||||
|
// combineLatestWith + distinctUntilChanged
|
||||||
|
// here the problem is described in more detail
|
||||||
|
// https://github.com/ReactiveX/rxjs/issues/7068
|
||||||
|
export const withLatestFromSynchronousFix =
|
||||||
|
<T, A extends readonly unknown[]>(...secondaries$: [...ObservableInputTuple<A>]) =>
|
||||||
|
(primary$: Observable<T>) =>
|
||||||
|
primary$.pipe(
|
||||||
|
// we add the index, so we can distinguish
|
||||||
|
// primary submissions from each other,
|
||||||
|
// and then we can only emit when primary changes
|
||||||
|
map((primary, i) => <const>[primary, i]),
|
||||||
|
combineLatestWith(...secondaries$),
|
||||||
|
distinctUntilChanged(undefined!, ([[_, i]]) => i),
|
||||||
|
map(([[primary], ...secondaries]) => <const>[primary, ...secondaries]),
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user