diff --git a/packages/firebase-server/src/lib/auth/auth.nest.spec.ts b/packages/firebase-server/src/lib/auth/auth.nest.spec.ts index 1764282f6..f41ab6efa 100644 --- a/packages/firebase-server/src/lib/auth/auth.nest.spec.ts +++ b/packages/firebase-server/src/lib/auth/auth.nest.spec.ts @@ -3,13 +3,15 @@ import { Module } from '@nestjs/common'; import { firebaseServerAuthModuleMetadata } from './auth.nest'; import { authorizedUserContextFactory, firebaseAdminFunctionNestContextFactory, initFirebaseServerAdminTestEnvironment } from "@dereekb/firebase-server/test"; import { AbstractFirebaseServerAuthContext, AbstractFirebaseServerAuthService, AbstractFirebaseServerAuthUserContext } from './auth.service'; -import { AuthClaims, authRoleClaimsService, AuthRoleSet, AUTH_ADMIN_ROLE, AUTH_ROLE_CLAIMS_DEFAULT_CLAIM_VALUE, objectHasNoKeys } from '@dereekb/util'; +import { AuthClaims, AuthClaimsUpdate, authRoleClaimsService, AuthRoleSet, AUTH_ADMIN_ROLE, AUTH_ROLE_CLAIMS_DEFAULT_CLAIM_VALUE, objectHasNoKeys } from '@dereekb/util'; import { CallableContextWithAuthData } from '../function/context'; const TEST_CLAIMS_SERVICE_CONFIG = { 'a': { roles: [AUTH_ADMIN_ROLE] } }; +type TestAuthClaims = typeof TEST_CLAIMS_SERVICE_CONFIG; + export class TestFirebaseServerAuthUserContext extends AbstractFirebaseServerAuthUserContext { } export class TestFirebaseServerAuthContext extends AbstractFirebaseServerAuthContext { } export class TestAuthService extends AbstractFirebaseServerAuthService { @@ -28,7 +30,7 @@ export class TestAuthService extends AbstractFirebaseServerAuthService { return TestAuthService.TEST_CLAIMS_SERVICE.toClaims(roles); } @@ -129,17 +131,19 @@ describe('firebase server auth', () => { describe('loadClaims()', () => { it('should load claims for the user.', async () => { - let claims = await authUserContext.loadClaims(); + const data = { + test: 1 + }; + + let claims = await authUserContext.loadClaims(); expect(claims).toBeDefined(); expect(objectHasNoKeys(claims)).toBe(true); - await authUserContext.setClaims({ - test: 1 - }); + await authUserContext.setClaims(data); - claims = await authUserContext.loadClaims(); + claims = await authUserContext.loadClaims(); expect(claims).toBeDefined(); - expect(claims!.test).toBe(1); + expect(claims.test).toBe(1); }); }); @@ -147,13 +151,19 @@ describe('firebase server auth', () => { describe('updateClaims()', () => { it('should update the existing claims.', async () => { + const data = { + test: 1, + second: null + }; + await authUserContext.setClaims({ test: 1 }); - let claims = await authUserContext.loadClaims(); + let claims = await authUserContext.loadClaims(); expect(claims).toBeDefined(); expect(claims!.test).toBe(1); + expect(claims!.second).not.toBe(2); await authUserContext.updateClaims({ second: 2 @@ -166,12 +176,17 @@ describe('firebase server auth', () => { }); it('should remove any keys with null update values', async () => { + const data = { + test: 1, + second: null + }; + await authUserContext.setClaims({ test: 1, second: 2 }); - let claims = await authUserContext.loadClaims(); + let claims = await authUserContext.loadClaims(); expect(claims).toBeDefined(); expect(claims!.test).toBe(1); @@ -190,11 +205,15 @@ describe('firebase server auth', () => { describe('clearClaims()', () => { it('should clear the claims.', async () => { + const data = { + test: 1 + }; + await authUserContext.setClaims({ test: 1 }); - let claims = await authUserContext.loadClaims(); + let claims = await authUserContext.loadClaims(); expect(claims).toBeDefined(); expect(claims!.test).toBe(1); diff --git a/packages/firebase-server/src/lib/auth/auth.service.ts b/packages/firebase-server/src/lib/auth/auth.service.ts index 0fdc387a7..9ba36e5e4 100644 --- a/packages/firebase-server/src/lib/auth/auth.service.ts +++ b/packages/firebase-server/src/lib/auth/auth.service.ts @@ -39,6 +39,11 @@ export interface FirebaseServerAuthUserContext extends FirebaseServerAuthUserIde */ removeRoles(roles: ArrayOrValue): Promise; + /** + * Loads the claims from the user. + */ + loadClaims(): Promise>; + /** * Updates the claims for a user by merging existing claims in with the input. * @@ -103,8 +108,8 @@ export abstract class AbstractFirebaseServerAuthUserContext { - return this.loadRecord().then(x => x.customClaims ?? {}); + loadClaims(): Promise> { + return this.loadRecord().then(x => (x.customClaims ?? {}) as AuthClaims); } async updateClaims(claims: AuthClaimsUpdate): Promise { @@ -253,7 +258,7 @@ export abstract class FirebaseServerAuthService { }); it('should not return a filter if a default filter obs that has no value is set for a key.', (done) => { - filterMap.addDefaultFilterObs(testKey, of({})); + filterMap.addDefaultFilterObs(testKey, undefined); filterMap.filterForKey(testKey).pipe(timeout({ first: 200, with: () => of(0) }), first()).subscribe((filter) => { expect(filter).toBe(0); done(); diff --git a/packages/rxjs/src/lib/filter/filter.map.ts b/packages/rxjs/src/lib/filter/filter.map.ts index cfddadf69..e3ca2c277 100644 --- a/packages/rxjs/src/lib/filter/filter.map.ts +++ b/packages/rxjs/src/lib/filter/filter.map.ts @@ -3,7 +3,7 @@ import { ObservableOrValue } from '../rxjs/getter'; import { FilterSourceInstance } from './filter.source'; import { BehaviorSubject, Observable, switchMap, map, distinctUntilChanged, shareReplay, first } from 'rxjs'; import { FilterSource, FilterSourceConnector } from './filter'; -import { Destroyable } from '@dereekb/util'; +import { Destroyable, Maybe } from '@dereekb/util'; export type FilterMapKey = string; @@ -24,7 +24,7 @@ export class FilterMap implements Destroyable { ); } - addDefaultFilterObs(key: FilterMapKey, obs: ObservableOrValue): void { + addDefaultFilterObs(key: FilterMapKey, obs: Maybe>): void { this._itemForKey(key).setDefaultFilterObs(obs); } @@ -85,7 +85,7 @@ class FilterMapItem { constructor(readonly dbxFilterMap: FilterMap, readonly key: FilterMapKey) { } - setDefaultFilterObs(obs: ObservableOrValue): void { + setDefaultFilterObs(obs: Maybe>): void { this._source.setDefaultFilter(obs); } diff --git a/packages/rxjs/src/lib/loading/loading.state.spec.ts b/packages/rxjs/src/lib/loading/loading.state.spec.ts index 38d3b8ea7..c58e034f7 100644 --- a/packages/rxjs/src/lib/loading/loading.state.spec.ts +++ b/packages/rxjs/src/lib/loading/loading.state.spec.ts @@ -34,7 +34,8 @@ describe('errorResult()', () => { it('should return a loading state that has the error.', () => { const error = { message: '' }; const state = errorResult(error); - expect(state.error).toBe(error); + expect(state.error).toBeDefined(); + expect(state.error?._error).toBe(error); }); it('should return a loading state that is not loading.', () => { diff --git a/packages/util/src/lib/auth/auth.role.claims.ts b/packages/util/src/lib/auth/auth.role.claims.ts index cd28dade0..5e11aa606 100644 --- a/packages/util/src/lib/auth/auth.role.claims.ts +++ b/packages/util/src/lib/auth/auth.role.claims.ts @@ -3,6 +3,7 @@ import { forEachKeyValue, objectHasKey } from "../object/object"; import { objectToTuples } from '../object/object.map'; import { ArrayOrValue, asArray } from '../array/array'; import { addToSet, setContainsAllValues } from '../set'; +import { Maybe } from '../value/maybe'; /** * Key in the claims. @@ -36,9 +37,9 @@ export type AuthClaims = { /** * A claims update. */ -export type AuthClaimsUpdate = { - [K in keyof Partial]: AuthClaimValue | ClearAuthClaimValue; -}; +export type AuthClaimsUpdate = Partial<{ + [K in keyof T]: AuthClaimValue | ClearAuthClaimValue; +}>; /** * Configuration for a claims key. diff --git a/packages/util/src/lib/error/error.ts b/packages/util/src/lib/error/error.ts index 421bf74d1..9a81da75e 100644 --- a/packages/util/src/lib/error/error.ts +++ b/packages/util/src/lib/error/error.ts @@ -11,6 +11,11 @@ export type StringErrorCode = string; */ export interface CodedError { code: StringErrorCode; + + /** + * The original error, if available. + */ + _error?: unknown; } /** @@ -54,12 +59,14 @@ export function convertToReadableError(inputError: Maybe): Maybe