Browse Source

[CL-298] feat: add error handling

CL-298-async-actions-2-0-context-string
Andreas Coroiu 2 years ago
parent
commit
9f014154cc
No known key found for this signature in database
GPG Key ID: E70B5FFC81DFEC1A
  1. 20
      libs/components/src/async-actions/async-actions.service.spec.ts
  2. 14
      libs/components/src/async-actions/async-actions.service.ts

20
libs/components/src/async-actions/async-actions.service.spec.ts

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
import { MockProxy, mock } from "jest-mock-extended";
import {
Observable,
Subject,
@ -9,13 +10,20 @@ import { @@ -9,13 +10,20 @@ import {
take,
} from "rxjs";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { AsyncActionsService } from "./async-actions.service";
describe("AsyncActionsService", () => {
let validationService!: MockProxy<ValidationService>;
let logService!: MockProxy<LogService>;
let service!: AsyncActionsService;
beforeEach(() => {
service = new AsyncActionsService();
validationService = mock();
logService = mock();
service = new AsyncActionsService(validationService, logService);
});
describe("state$", () => {
@ -88,13 +96,13 @@ describe("AsyncActionsService", () => { @@ -88,13 +96,13 @@ describe("AsyncActionsService", () => {
});
describe("execute", () => {
it("executes the handler and re-throws any errors", async () => {
it("catches and handles all errors using the supplied log and validation services", async () => {
const error = new Error("example");
const result = async () =>
await service.execute("context", Symbol(), () => Promise.reject(error));
await service.execute("context", Symbol(), () => Promise.reject(error));
await expect(result).rejects.toThrow(error);
expect(logService.error).toHaveBeenCalledWith(expect.anything(), error);
expect(validationService.showError).toHaveBeenCalledWith(error);
});
it("unsubscribes from the observable when the service is destroyed", async () => {
@ -117,6 +125,8 @@ describe("AsyncActionsService", () => { @@ -117,6 +125,8 @@ describe("AsyncActionsService", () => {
expect(action.observed).toBe(false);
});
it("ignores EmptyError and completes the action", async () => {});
});
});

14
libs/components/src/async-actions/async-actions.service.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { Injectable, OnDestroy } from "@angular/core";
import { Injectable, OnDestroy, Optional } from "@angular/core";
import {
BehaviorSubject,
EmptyError,
@ -11,6 +11,10 @@ import { @@ -11,6 +11,10 @@ import {
takeUntil,
} from "rxjs";
// TODO: This service should move out of CL and into platform, so these imports won't be an issue in the long term.
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { FunctionReturningAwaitable, functionToObservable } from "../utils/function-to-observable";
export type ContextState = { status: "inactive" } | { status: "active"; origin: unknown };
@ -24,6 +28,11 @@ export class AsyncActionsService implements OnDestroy { @@ -24,6 +28,11 @@ export class AsyncActionsService implements OnDestroy {
private readonly onDestroy$ = new Subject<void>();
private readonly states$ = new BehaviorSubject<ServiceState>({});
constructor(
@Optional() private validationService?: ValidationService,
@Optional() private logService?: LogService,
) {}
/**
* Emits the current state of the context.
* - When no action has been executed, it emits `{ status: "inactive" }`.
@ -74,7 +83,8 @@ export class AsyncActionsService implements OnDestroy { @@ -74,7 +83,8 @@ export class AsyncActionsService implements OnDestroy {
return;
}
throw error;
this.logService?.error(`Async action exception: ${error}`, error);
this.validationService?.showError(error);
} finally {
this.removeState(context);
}

Loading…
Cancel
Save