Skip to content

Commit

Permalink
Add actor error handling
Browse files Browse the repository at this point in the history
Signed-off-by: Marcos Candeia <[email protected]>
  • Loading branch information
mcandeia committed Oct 19, 2024
1 parent fd708ec commit 7d0bb2e
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 4 deletions.
8 changes: 6 additions & 2 deletions src/actors/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ export interface ActorsServer {
deploymentId?: string;
}

export interface ActorsOptions {
server?: ActorsServer;
errorHandling?: Record<string, new (...args: unknown[]) => Error>;
}
/**
* utilities to create and manage actors.
*/
export const actors = {
proxy: <TInstance extends Actor>(
actor: ActorConstructor<TInstance> | string,
server?: ActorsServer | undefined,
options?: ActorsOptions | undefined,
): { id: (id: string) => Promisify<TInstance> } => {
const factory = (id: string) => createHttpInvoker(id, server);
const factory = (id: string) => createHttpInvoker(id, options);
return create(actor, factory);
},
};
24 changes: 22 additions & 2 deletions src/actors/proxyutil.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ActorsServer } from "./proxy.ts";
import type { ActorsOptions, ActorsServer } from "./proxy.ts";
import type { Actor, ActorConstructor } from "./runtime.ts";
import { EVENT_STREAM_RESPONSE_HEADER, readFromStream } from "./stream.ts";
import {
Expand All @@ -9,6 +9,7 @@ import {

export const ACTOR_ID_HEADER_NAME = "x-deno-isolate-instance-id";
export const ACTOR_ID_QS_NAME = "deno_isolate_instance_id";
export const ACTOR_CONSTRUCTOR_NAME_HEADER = "x-error-constructor-name";
/**
* Promise.prototype.then onfufilled callback type.
*/
Expand Down Expand Up @@ -177,8 +178,9 @@ export const createHttpInvoker = <
TChannel extends DuplexChannel<unknown, unknown>,
>(
actorId: string,
server?: ActorsServer,
options?: ActorsOptions,
): ActorInvoker<TResponse, TChannel> => {
const server = options?.server;
if (!server) {
_server ??= initServer();
}
Expand Down Expand Up @@ -214,6 +216,24 @@ export const createHttpInvoker = <
}),
},
);
if (!resp.ok) {
const constructorName = resp.headers.get(ACTOR_CONSTRUCTOR_NAME_HEADER);
const ErrorConstructor =
options?.errorHandling?.[constructorName ?? "Error"] ?? Error;
const errorParameters =
resp.headers.get("content-type")?.includes("application/json")
? await resp.json()
: {
message: await resp.text().catch(() =>
`HTTP Error: ${resp.statusText}`
),
};
const deserializedError = Object.assign(
new ErrorConstructor(),
errorParameters,
);
throw deserializedError;
}
if (
resp.headers.get("content-type") ===
EVENT_STREAM_RESPONSE_HEADER
Expand Down
15 changes: 15 additions & 0 deletions src/actors/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type ServerSentEventMessage, ServerSentEventStream } from "@std/http";
import { ActorError } from "./errors.ts";
import {
ACTOR_CONSTRUCTOR_NAME_HEADER,
ACTOR_ID_HEADER_NAME,
ACTOR_ID_QS_NAME,
type ActorInvoker,
Expand Down Expand Up @@ -278,6 +279,20 @@ export class ActorRuntime {
}[err.code] ?? 500,
});
}
const constructorName = err?.constructor?.name;
if (constructorName) {
const serializedError = JSON.stringify(
err,
Object.getOwnPropertyNames(err),
);
return new Response(serializedError, {
status: 500,
headers: {
[ACTOR_CONSTRUCTOR_NAME_HEADER]: constructorName,
"content-type": "application/json",
},
});
}
throw err;
}
}
Expand Down

0 comments on commit 7d0bb2e

Please sign in to comment.