Skip to content

Commit

Permalink
refactor: refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Fifciu committed Oct 29, 2024
1 parent 2a6cfd4 commit 6e59b5b
Show file tree
Hide file tree
Showing 12 changed files with 598 additions and 570 deletions.
758 changes: 377 additions & 381 deletions packages/middleware/__tests__/integration/loggerErrorBoundaries.spec.ts

Large diffs are not rendered by default.

20 changes: 18 additions & 2 deletions packages/middleware/__tests__/unit/handlers/getApiFunction.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { markExtensionNameHelpers } from "../../../src/apiClientFactory/markExtensionNameHelpers";
import { getApiFunction } from "../../../src/handlers/prepareApiFunction/getApiFunction";

describe("getApiFunction", () => {
Expand All @@ -12,23 +13,38 @@ describe("getApiFunction", () => {
},
};

markExtensionNameHelpers.mark(
apiClient.api.extension1.function1 as any,
"extension1"
);
markExtensionNameHelpers.mark(
apiClient.api.extension1.function2 as any,
"extension1"
);

it("should return the correct API function when extensionName is provided", async () => {
const functionName = "function1";
const extensionName = "extension1";
const expectedApiFn = apiClient.api[extensionName][functionName];

const apiFn = await getApiFunction(apiClient, functionName, extensionName);
const [apiFn, fnOrigin] = await getApiFunction(
apiClient,
functionName,
extensionName
);

expect(apiFn).toEqual(expectedApiFn);
expect(fnOrigin).toEqual(extensionName);
});

it("should return the correct API function when extensionName is not provided", async () => {
const functionName = "function3";
const expectedApiFn = apiClient.api[functionName];

const apiFn = await getApiFunction(apiClient, functionName);
const [apiFn, fnOrigin] = await getApiFunction(apiClient, functionName);

expect(apiFn).toEqual(expectedApiFn);
expect(fnOrigin).not.toBeDefined();
});

it("should throw an error when the API function is not found with extensionName", async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ describe("[getInitConfig]", () => {
});

it("should return an empty object when apiClient is not defined", async () => {
const params = {} as unknown as LoadInitConfigProps;
const params = {
alokai: {
logger,
},
} as unknown as LoadInitConfigProps;
const result = await getInitConfig(params);

expect(result).toEqual({});
Expand Down
84 changes: 44 additions & 40 deletions packages/middleware/src/apiClientFactory/applyContextToApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { createExtendQuery } from "./createExtendQuery";
import { markExtensionNameHelpers } from "./markExtensionNameHelpers";
import { getLogger, injectMetadata } from "../logger";
import { wrapFnWithErrorBoundary } from "./wrapFnWithErrorBoundary";

const nopBefore = <ARGS>({ args }: BeforeCallParams<any, ARGS>): ARGS => args;
const nopAfter = <RESPONSE>({
Expand Down Expand Up @@ -48,32 +49,6 @@ function injectHandlerMetadata<CONTEXT extends MiddlewareContext>(
});
}

async function injectHandlerErrorMetadata<CONTEXT extends MiddlewareContext>(
fn: Function,
args: any[],
context: CONTEXT,
callName: string
) {
try {
const response = await fn(...args);
return response;
} catch (err) {
const errorBoundary: LogScope = err.errorBoundary || {
type: "endpoint" as const,
functionName: callName,
integrationName: context.integrationTag,
};

if (!err.errorBoundary && markExtensionNameHelpers.has(fn)) {
errorBoundary.extensionName = markExtensionNameHelpers.get(fn);
}

Object.assign(err, { errorBoundary });

throw err;
}
}

/**
* Wraps api methods with context and hooks triggers
*/
Expand All @@ -94,27 +69,51 @@ const applyContextToApi = <
(prev, [callName, fn]) => ({
...prev,
[callName]: (() => {
const newFn = async (...args: Parameters<typeof fn>) => {
const logger = injectHandlerMetadata(context, fn, callName);
/**
* Endpoint's handler decorated with:
* - hooks from every extension (hooks.before contains merged beforeCall's,
* hooks.after contains merged afterCall's),
* - logger with metadata about scope of call (covers orchestrated parallely called endpoints),
* - support for building error boundary (covers orchestrated parallely called endpoints),
*/
const handler = async (...args: Parameters<typeof fn>) => {
const transformedArgs = await hooks.before({
callName,
args,
});

const fnWithErrorBoundary = wrapFnWithErrorBoundary(fn, (err) => {
/**
* Handlers can call different handlers, so error could be already bubbling.
* That's why we check for presence of err.errorBoundary at first.
*
* ```ts
* async function myEndpoint(context) {
* const int = await context.getApiClient("some_integration");
* return await int.api.throwError(); // Bubbling from other integration's endpoint
* }
* ```
*/
const errorBoundary: LogScope = err.errorBoundary || {
type: "endpoint" as const,
functionName: callName,
integrationName: context.integrationTag,
...(markExtensionNameHelpers.has(fn)
? { extensionName: markExtensionNameHelpers.get(fn) }
: {}),
};

return errorBoundary;
});
const apiClientContext = {
...context,
extendQuery: createExtendQuery(context),
logger,
};
const response = await injectHandlerErrorMetadata(
fn,
[
{
...apiClientContext,
logger: injectHandlerMetadata(context, fn, callName),
},
...transformedArgs,
],
context,
callName
const response = await fnWithErrorBoundary(
apiClientContext,
...transformedArgs
);

const transformedResponse = await hooks.after({
Expand All @@ -125,13 +124,18 @@ const applyContextToApi = <

return transformedResponse;
};

/**
* Marks decorated handler with name of integration's extension
* from which original handler was defined, if any
*/
if (markExtensionNameHelpers.has(fn)) {
markExtensionNameHelpers.mark(
newFn,
handler,
markExtensionNameHelpers.get(fn)
);
}
return newFn;
return handler;
})(),
}),
{} as any
Expand Down
Loading

0 comments on commit 6e59b5b

Please sign in to comment.