Skip to content

Commit

Permalink
feat: add option to OrGuard to throw the last error
Browse files Browse the repository at this point in the history
  • Loading branch information
p-dim-popov authored and jmcdo29 committed Dec 10, 2024
1 parent eca3c46 commit c050d97
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 9 deletions.
5 changes: 5 additions & 0 deletions packages/or-guard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,18 @@ OrGuard(guards: Array<Type<CanActivate> | InjectionToken>, orGuardOptions?: OrGu
```ts
interface OrGuardOptions {
throwOnFirstError?: boolean;
throwLastError?: boolean;
}
```

- `throwOnFirstError`: a boolean to tell the `OrGuard` whether to throw if an
error is encountered or if the error should be considered a `return false`.
The default value is `false`. If this is set to `true`, the **first** error
encountered will lead to the same error being thrown.
- `throwLastError`: a boolean to tell the `OrGuard` if the last error should be
handled with `return false` or just thrown. The default value is `false`. If
this is set to `true`, the **last** error encountered will lead to the same
error being thrown.

> **Note**: guards are ran in a non-deterministic order. All guard returns are
> transformed into Observables and ran concurrently to ensure the fastest
Expand Down
26 changes: 17 additions & 9 deletions packages/or-guard/src/lib/or.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@ import {
} from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import {
concatMap,
defer,
from,
map,
Observable,
of,
OperatorFunction,
throwError,
pipe,
} from 'rxjs';
import { catchError, last, mergeMap, takeWhile } from 'rxjs/operators';

interface OrGuardOptions {
throwOnFirstError?: boolean;
throwLastError?: boolean;
}

export function OrGuard(
Expand All @@ -39,8 +43,9 @@ export function OrGuard(
mergeMap((obs) => {
return obs.pipe(this.handleError());
}),
takeWhile((val) => val === false, true),
last()
takeWhile(({ result }) => result === false, true),
last(),
concatMap(({ result, error }) => result === false && orGuardOptions?.throwLastError && error ? throwError(() => error) : of(result))
);
}

Expand All @@ -60,13 +65,16 @@ export function OrGuard(
});
}

private handleError(): OperatorFunction<boolean, boolean> {
return catchError((err) => {
if (orGuardOptions?.throwOnFirstError) {
return throwError(() => err);
}
return of(false);
});
private handleError(): OperatorFunction<boolean, { result: boolean, error?: unknown }> {
return pipe(
catchError((error) => {
if (orGuardOptions?.throwOnFirstError) {
return throwError(() => error);
}
return of({ result: false, error });
}),
map((result) => typeof result === 'boolean' ? { result } : result)
);
}

private guardIsPromise(
Expand Down
6 changes: 6 additions & 0 deletions packages/or-guard/test/app.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ export class AppController {
return this.message;
}

@UseGuards(OrGuard([SyncGuard, ThrowGuard], { throwLastError: true }))
@Get('throw-last')
getThrowGuardThrowLast() {
return this.message;
}

@UseGuards(OrGuard(['SyncAndProm', ObsGuard]))
@Get('logical-and')
getLogicalAnd() {
Expand Down
22 changes: 22 additions & 0 deletions packages/or-guard/test/or.guard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,28 @@ describe('OrGuard and AndGuard Integration Test', () => {
});
});
});
describe('throw-last', () => {
/**
* OrGuard([SyncGuard, ThrowGuard], { throwLastError: true})
*
* | Sync | Throw | Final |
* | - | - | - |
* | true | UnauthorizedException | false |
* | false | UnauthorizedException | false |
*/
it('should throw the last error', async () => {
return supertest(app.getHttpServer())
.get('/throw-last')
.expect(sync ? 200 : 401)
.expect(({ body }) => {
if (!sync) {
expect(body).toEqual(
expect.objectContaining({ message: 'ThrowGuard' })
);
}
});
});
});
});
}
);
Expand Down

0 comments on commit c050d97

Please sign in to comment.