Skip to content

Commit

Permalink
feat: codedError now includes original error if available
Browse files Browse the repository at this point in the history
  • Loading branch information
dereekb committed May 23, 2022
1 parent 998502b commit 1262281
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 25 deletions.
41 changes: 30 additions & 11 deletions packages/firebase-server/src/lib/auth/auth.nest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TestAuthService> { }
export class TestFirebaseServerAuthContext extends AbstractFirebaseServerAuthContext<TestFirebaseServerAuthContext, TestFirebaseServerAuthUserContext, TestAuthService> { }
export class TestAuthService extends AbstractFirebaseServerAuthService<TestFirebaseServerAuthUserContext, TestFirebaseServerAuthContext> {
Expand All @@ -28,7 +30,7 @@ export class TestAuthService extends AbstractFirebaseServerAuthService<TestFireb
return TestAuthService.TEST_CLAIMS_SERVICE.toRoles(claims);
}

claimsForRoles(roles: AuthRoleSet): AuthClaims {
claimsForRoles(roles: AuthRoleSet): AuthClaimsUpdate<TestAuthClaims> {
return TestAuthService.TEST_CLAIMS_SERVICE.toClaims(roles);
}

Expand Down Expand Up @@ -129,31 +131,39 @@ 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<typeof data>();
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<typeof data>();
expect(claims).toBeDefined();
expect(claims!.test).toBe(1);
expect(claims.test).toBe(1);
});

});

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<typeof data>();
expect(claims).toBeDefined();
expect(claims!.test).toBe(1);
expect(claims!.second).not.toBe(2);

await authUserContext.updateClaims({
second: 2
Expand All @@ -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<typeof data>();
expect(claims).toBeDefined();
expect(claims!.test).toBe(1);

Expand All @@ -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<typeof data>();
expect(claims).toBeDefined();
expect(claims!.test).toBe(1);

Expand Down
13 changes: 9 additions & 4 deletions packages/firebase-server/src/lib/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export interface FirebaseServerAuthUserContext extends FirebaseServerAuthUserIde
*/
removeRoles(roles: ArrayOrValue<AuthRole>): Promise<void>;

/**
* Loads the claims from the user.
*/
loadClaims<T = unknown>(): Promise<AuthClaims<T>>;

/**
* Updates the claims for a user by merging existing claims in with the input.
*
Expand Down Expand Up @@ -103,8 +108,8 @@ export abstract class AbstractFirebaseServerAuthUserContext<S extends FirebaseSe
return filterNullAndUndefinedValues(this.service.claimsForRoles(asSet(roles)));
}

loadClaims(): Promise<AuthClaims> {
return this.loadRecord().then(x => x.customClaims ?? {});
loadClaims<T = unknown>(): Promise<AuthClaims<T>> {
return this.loadRecord().then(x => (x.customClaims ?? {}) as AuthClaims<T>);
}

async updateClaims(claims: AuthClaimsUpdate): Promise<void> {
Expand Down Expand Up @@ -253,7 +258,7 @@ export abstract class FirebaseServerAuthService<U extends FirebaseServerAuthUser
*
* @param roles
*/
abstract claimsForRoles(roles: AuthRoleSet): AuthClaims;
abstract claimsForRoles(roles: AuthRoleSet): AuthClaimsUpdate;

}

Expand All @@ -280,6 +285,6 @@ export abstract class AbstractFirebaseServerAuthService<U extends FirebaseServer

abstract readRoles(claims: AuthClaims): AuthRoleSet;

abstract claimsForRoles(roles: AuthRoleSet): AuthClaims;
abstract claimsForRoles(roles: AuthRoleSet): AuthClaimsUpdate;

}
2 changes: 1 addition & 1 deletion packages/rxjs/src/lib/filter/filter.map.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('FilterMap', () => {
});

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();
Expand Down
6 changes: 3 additions & 3 deletions packages/rxjs/src/lib/filter/filter.map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -24,7 +24,7 @@ export class FilterMap<F> implements Destroyable {
);
}

addDefaultFilterObs(key: FilterMapKey, obs: ObservableOrValue<F>): void {
addDefaultFilterObs(key: FilterMapKey, obs: Maybe<ObservableOrValue<F>>): void {
this._itemForKey(key).setDefaultFilterObs(obs);
}

Expand Down Expand Up @@ -85,7 +85,7 @@ class FilterMapItem<F> {

constructor(readonly dbxFilterMap: FilterMap<F>, readonly key: FilterMapKey) { }

setDefaultFilterObs(obs: ObservableOrValue<F>): void {
setDefaultFilterObs(obs: Maybe<ObservableOrValue<F>>): void {
this._source.setDefaultFilter(obs);
}

Expand Down
3 changes: 2 additions & 1 deletion packages/rxjs/src/lib/loading/loading.state.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.', () => {
Expand Down
7 changes: 4 additions & 3 deletions packages/util/src/lib/auth/auth.role.claims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -36,9 +37,9 @@ export type AuthClaims<T = object> = {
/**
* A claims update.
*/
export type AuthClaimsUpdate<T = object> = {
[K in keyof Partial<T>]: AuthClaimValue | ClearAuthClaimValue;
};
export type AuthClaimsUpdate<T = object> = Partial<{
[K in keyof T]: AuthClaimValue | ClearAuthClaimValue;
}>;

/**
* Configuration for a claims key.
Expand Down
11 changes: 9 additions & 2 deletions packages/util/src/lib/error/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export type StringErrorCode = string;
*/
export interface CodedError {
code: StringErrorCode;

/**
* The original error, if available.
*/
_error?: unknown;
}

/**
Expand Down Expand Up @@ -54,12 +59,14 @@ export function convertToReadableError(inputError: Maybe<ErrorInput>): Maybe<Cod
} else if (inputError instanceof BaseError) {
error = {
code: inputError.name,
message: inputError.message
message: inputError.message,
_error: inputError
};
} else {
error = {
code: 'ERROR',
message: (inputError as ReadableError).message || ''
message: (inputError as ReadableError).message || '',
_error: inputError
};
}
}
Expand Down

0 comments on commit 1262281

Please sign in to comment.