Skip to content

Commit

Permalink
refactor: reduced usage of singleton to support parallel calls of fns
Browse files Browse the repository at this point in the history
  • Loading branch information
Fifciu committed Oct 18, 2024
1 parent eb6c17f commit f808e08
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 372 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { success } from "./success";
export { successParalell } from "./successParalell";
export { setCookieHeader } from "./setCookieHeader";
export { error } from "./error";
export { throwAxiosError } from "./throwAxiosError";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { getLogger } from "../../../../../src";

export const successParalell = (context) => {
const logger = getLogger(context);
logger.info("successParalell");
return Promise.resolve({
status: 200,
message: "ok",
error: false,
});
};
75 changes: 72 additions & 3 deletions packages/middleware/__tests__/integration/loggerScopes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,47 @@ const testingExtension = {
},
async resueOtherIntegrationMethod(context) {
const logger = getLogger(context);
logger.info("some log");
logger.info("resueOtherIntegrationMethod log");
const int = await context.getApiClient("replicated_integration");
return await int.api[
"replicated-integration-extension"
].orchestrationTest();
},
async paralellOrchestrationTest(context) {
const logger = getLogger(context);
logger.info("resueOtherIntegrationMethod log");
// paralell_replicated_integration
const int = await context.getApiClient("replicated_integration");
const int2 = await context.getApiClient(
"paralell_replicated_integration"
);
// await int.api["replicated-integration-extension"].orchestrationTest();
// await int2.api.successParalell();
return await Promise.all([
int.api["replicated-integration-extension"].orchestrationTest(),
int2.api.successParalell(),
]);
},
},
hooks(req, res, alokai) {
const logger = getLogger(alokai);
logger.info("hooks");
return {
beforeCall({ args }) {
beforeCall({ args, logger }) {
logger.info("bc1");
return args;
},
beforeCreate(a) {
const logger = getLogger(a);
logger.info("bc2");
return a;
},
afterCall({ response }) {
afterCall({ response, logger }) {
logger.info("after1");
return response;
},
afterCreate(a) {
const logger = getLogger(a);
logger.info("after2");
return a;
},
Expand Down Expand Up @@ -132,6 +149,19 @@ describe("[Integration] Logger scopes", () => {
];
},
},
paralell_replicated_integration: {
configuration: {},
location: "./__tests__/integration/bootstrap/server",
logger: {
handler: Logger,
},
extensions() {
return [
testingExtension as any,
replicatedIntegrationExtension as any,
];
},
},
},
});
});
Expand Down Expand Up @@ -339,6 +369,45 @@ describe("[Integration] Logger scopes", () => {
});
});

test("scope of log from: multiple different integrations called from extension", async () => {
await request(app).post("/test_integration/paralellOrchestrationTest");

expect(Logger.info).toBeCalledWith(expect.any(String), {
context: "middleware",
scope: {
extensionName: "testing-extension",
functionName: "paralellOrchestrationTest",
integrationName: "test_integration",
type: "endpoint",
},
});
expect(Logger.info).toBeCalledWith(expect.any(String), {
context: "middleware",
scope: {
extensionName: "replicated-integration-extension",
functionName: "orchestrationTest",
integrationName: "replicated_integration",
type: "endpoint",
},
});
expect(Logger.info).toBeCalledWith(expect.any(String), {
context: "middleware",
scope: {
functionName: "success",
integrationName: "replicated_integration",
type: "endpoint",
},
});
expect(Logger.info).toBeCalledWith(expect.any(String), {
context: "middleware",
scope: {
functionName: "successParalell",
integrationName: "paralell_replicated_integration",
type: "endpoint",
},
});
});

test("scope of log extendApp", async () => {
await createServer({
logger: {
Expand Down
82 changes: 53 additions & 29 deletions packages/middleware/src/apiClientFactory/applyContextToApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,46 @@ import {
BeforeCallParams,
MiddlewareContext,
} from "../types";
import {
ExtensionEndpointMethodDecorator,
PurgeHookNameMethodDecorator,
MethodDecoratorManager,
MissingScopeMethodDecorator,
OrchiestratedMethodDecorator,
ScopeTypeMethodDecorator,
} from "./methodDecorator";
import { createExtendQuery } from "./createExtendQuery";
import { markExtensionNameHelpers } from "./markExtensionNameHelpers";
import { getLogger, injectMetadata } from "../logger";

const nopBefore = <ARGS>({ args }: BeforeCallParams<any, ARGS>): ARGS => args;
const nopAfter = <RESPONSE>({
response,
}: AfterCallParams<any, any, RESPONSE>) => response;

const checkMetadataScope = <CONTEXT extends MiddlewareContext>(
context: CONTEXT
) => Boolean(context.res.locals?.alokai?.metadata?.scope);
/**
* @returns Instance of Logger with injected metadata of currently called handler and it's integration
*/
function injectHandlerMetadata<CONTEXT extends MiddlewareContext>(
context: CONTEXT,
fn: Function,
callName: string
) {
return injectMetadata(getLogger(context), (metadata) => {
const newMetadata = {
...metadata,
scope: {
...metadata?.scope,
type: "endpoint" as const,
/**
* The following lines ensure proper functionality in case of orchestration.
* In this scenario, there are multiple self-invocations of the function template we see here.
* Adding the currently known functionName and integrationName prevents scope confusion
* between different recurrent synchronous and parallel invocations (e.g., multiple orchestrated methods
* executed with Promise.all).
*/
functionName: callName,
integrationName: context.integrationTag,
},
};
if (markExtensionNameHelpers.has(fn)) {
newMetadata.scope.extensionName = markExtensionNameHelpers.get(fn);
}
return newMetadata;
});
}

/**
* Wraps api methods with context and hooks triggers
Expand All @@ -44,27 +67,28 @@ const applyContextToApi = <
(prev, [callName, fn]) => ({
...prev,
[callName]: async (...args: Parameters<typeof fn>) => {
const methodDecoratorManager = new MethodDecoratorManager(
context,
hooks,
const transformedArgs = await hooks.before({
callName,
fn
args,
});
const apiClientContext = {
...context,
extendQuery: createExtendQuery(context),
};
const response = await fn(
{
...apiClientContext,
logger: injectHandlerMetadata(context, fn, callName),
},
...transformedArgs
);
const transformedResponse = await hooks.after({
callName,
args,
response,
});

const hasMetadataScope = checkMetadataScope(context);
const decoratorsToAdd = hasMetadataScope
? [
new ScopeTypeMethodDecorator(context),
new PurgeHookNameMethodDecorator(context),
new ExtensionEndpointMethodDecorator(context, fn),
new OrchiestratedMethodDecorator(context, callName, fn),
]
: [new MissingScopeMethodDecorator(context, callName, fn.name)];
methodDecoratorManager.addDecorator(...decoratorsToAdd);

const method = methodDecoratorManager.prepare<typeof args>();

return await method(args);
return transformedResponse;
},
}),
{} as any
Expand Down
Loading

0 comments on commit f808e08

Please sign in to comment.