Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/engine/executionEngine.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import { TraceableEngine } from './traceableEngine';
import { EngineTrace } from '../common/models/engineTrace.model';

Expand Down
2 changes: 1 addition & 1 deletion src/engine/traceableEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { DEFAULT_TRACE_CONFIG, TraceOptions } from '../common/models/engineTrace
import { ExecutionTrace, ExecutionTraceExtractor, isExecutionTrace } from '../common/models/executionTrace.model';
import { Awaited } from '../common/utils/awaited';
import { extract } from '../common/utils/jsonQuery';
import { executionTrace } from '../execution/trace';
import { ExecutionTimer } from '../timer/executionTimer';
import { executionTrace } from '../trace/trace';

/**
* Represents a class for traceable execution of functions.
Expand Down
3 changes: 2 additions & 1 deletion src/execution/memoize.decorator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ describe('memoize decorator', () => {
if (memoContext.isMemoized) {
memoizedCalls++;
}
}) async throwData(name: string): Promise<string> {
})
async throwData(name: string): Promise<string> {
totalFunctionCalls++;
throw new Error(`hello ${name} but I throw!`);
}
Expand Down
7 changes: 1 addition & 6 deletions src/execution/memoize.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { execute } from './execute';
import {
memoizationDefaultTTL,
memoizationKey,
memoizationMaxTTL,
MemoizeOptions
} from '../common/models/executionMemoization.model';
import { memoizationDefaultTTL, memoizationKey, memoizationMaxTTL, MemoizeOptions } from '../common/models/executionMemoization.model';
import { generateHashId } from '../common/utils/crypto';
import { extractFunctionMetadata } from '../common/utils/functionMetadata';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { trace } from './traceDecorator';
import { trace } from './trace.decorator';

describe('trace decorator', () => {
const executionTraceExpectation = {
Expand All @@ -21,15 +21,15 @@ describe('trace decorator', () => {
};

describe('Tracing synchronous functions', () => {
const traceHandlerMock = jest.fn();
const traceHandlerMockThrows = jest.fn();
const onTraceEventMock = jest.fn();
const onTraceEventMockThrows = jest.fn();

class SyncClass {
greetingFrom = ['hello from class'];

@trace(function (...args) {
this.greetingFrom.push('hello from trace handler');
traceHandlerMock(...args);
onTraceEventMock(...args);
expect(this.greetingFrom).toEqual(['hello from class', 'hello from method', 'hello from trace handler']);
})
helloWorld(): string {
Expand All @@ -38,7 +38,7 @@ describe('trace decorator', () => {
}

@trace((...args) => {
traceHandlerMockThrows(...args);
onTraceEventMockThrows(...args);
throw new Error('hello but I throw!');
})
helloWorldHandlerThrows(): string {
Expand All @@ -50,8 +50,8 @@ describe('trace decorator', () => {
it('should trace a synchronous function and verify context', () => {
const instance = new SyncClass();
expect(instance.helloWorld()).toBe('Hello World');
expect(traceHandlerMock).toHaveBeenCalledTimes(1);
expect(traceHandlerMock).toHaveBeenCalledWith(
expect(onTraceEventMock).toHaveBeenCalledTimes(1);
expect(onTraceEventMock).toHaveBeenCalledWith(
expect.objectContaining({
metadata: expect.objectContaining({
class: 'SyncClass',
Expand All @@ -69,8 +69,8 @@ describe('trace decorator', () => {
it('should trace a synchronous function and verify helloWorldHandlerThrows', () => {
const instance = new SyncClass();
expect(() => instance.helloWorldHandlerThrows()).toThrowError('hello but I throw!');
expect(traceHandlerMockThrows).toHaveBeenCalledTimes(1);
expect(traceHandlerMockThrows).toHaveBeenCalledWith(
expect(onTraceEventMockThrows).toHaveBeenCalledTimes(1);
expect(onTraceEventMockThrows).toHaveBeenCalledWith(
expect.objectContaining({
metadata: expect.objectContaining({
class: 'SyncClass',
Expand Down Expand Up @@ -124,11 +124,11 @@ describe('trace decorator', () => {
});
});

describe('Tracing function traceHandlerMock and traceContext', () => {
describe('Tracing function onTraceEventMock and traceContext', () => {
const traceContextDivision = { context: { metadata: { requestId: '12345' } } };
const traceContextFetchData = { context: { metadata: { requestId: '6789' } } };
const traceHandlerDivisionMock = jest.fn();
const traceHandlerFetchDataMock = jest.fn();
const onTraceEventDivisionMock = jest.fn();
const onTraceEventFetchDataMock = jest.fn();

function empty(): MethodDecorator {
return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) {
Expand All @@ -140,7 +140,7 @@ describe('trace decorator', () => {
}

class MyClass {
@trace(traceHandlerDivisionMock, traceContextDivision, { contextKey: '__execution' })
@trace(onTraceEventDivisionMock, traceContextDivision, { contextKey: '__execution' })
divisionFunction(x: number, y: number, traceContext: Record<string, unknown> = {}): number {
if (y === 0) {
traceContext['narratives'] = [`Throwing because division of ${x} by ${y}`];
Expand All @@ -150,7 +150,7 @@ describe('trace decorator', () => {
return x / y;
}

@trace(traceHandlerFetchDataMock, traceContextFetchData, { contextKey: '__execution' })
@trace(onTraceEventFetchDataMock, traceContextFetchData, { contextKey: '__execution' })
async fetchDataFunction(url: string, traceContext: Record<string, unknown> = {}): Promise<{ data: string }> {
traceContext['narratives'] = [`Fetching data from ${url}`];
if (!url.startsWith('http')) {
Expand All @@ -160,24 +160,24 @@ describe('trace decorator', () => {
return { data: 'Success' };
}

@trace(traceHandlerDivisionMock, traceContextDivision)
@trace(onTraceEventDivisionMock, traceContextDivision)
@empty()
divisionFunctionOnAnotherDecorator(x: number, y: number, traceContext: Record<string, unknown> = {}): number {
return this.divisionFunction(x, y, traceContext);
}

@trace(traceHandlerFetchDataMock, traceContextFetchData)
@trace(onTraceEventFetchDataMock, traceContextFetchData)
@empty()
async fetchDataFunctionOnAnotherDecorator(
url: string,
traceContext: Record<string, unknown> = {}
): Promise<{
data: string;
}> {
data: string;
}> {
return this.fetchDataFunction(url, traceContext);
}

@trace(traceHandlerFetchDataMock, traceContextFetchData, { errorStrategy: 'catch' })
@trace(onTraceEventFetchDataMock, traceContextFetchData, { errorStrategy: 'catch' })
@empty()
async fetchDataFunctionOnAnotherDecoratorCoughtError(
url: string,
Expand All @@ -188,14 +188,14 @@ describe('trace decorator', () => {
}

beforeEach(() => {
traceHandlerFetchDataMock.mockClear();
traceHandlerDivisionMock.mockClear();
onTraceEventFetchDataMock.mockClear();
onTraceEventDivisionMock.mockClear();
});

it('should sync trace successfully and pass correct traceHandlerMock and traceContext', () => {
it('should sync trace successfully and pass correct onTraceEventMock and traceContext', () => {
const classInstance = new MyClass();
const response = classInstance.divisionFunction(1, 2);
expect(traceHandlerDivisionMock).toHaveBeenCalledWith(
expect(onTraceEventDivisionMock).toHaveBeenCalledWith(
expect.objectContaining({
metadata: expect.objectContaining({
class: 'MyClass',
Expand All @@ -213,10 +213,10 @@ describe('trace decorator', () => {
expect(response).toEqual(0.5);
});

it('should sync trace errors and pass correct traceHandlerMock and traceContext', () => {
it('should sync trace errors and pass correct onTraceEventMock and traceContext', () => {
expect(() => new MyClass().divisionFunction(1, 0)).toThrow('Throwing because division by zero is not allowed.');

expect(traceHandlerDivisionMock).toHaveBeenCalledWith(
expect(onTraceEventDivisionMock).toHaveBeenCalledWith(
expect.objectContaining({
metadata: expect.objectContaining({
class: 'MyClass',
Expand All @@ -233,9 +233,9 @@ describe('trace decorator', () => {
);
});

it('should async trace successfully and pass correct traceHandlerMock and traceContext', async () => {
it('should async trace successfully and pass correct onTraceEventMock and traceContext', async () => {
const response = await new MyClass().fetchDataFunction('https://api.example.com/data');
expect(traceHandlerFetchDataMock).toHaveBeenCalledWith(
expect(onTraceEventFetchDataMock).toHaveBeenCalledWith(
expect.objectContaining({
metadata: expect.objectContaining({
class: 'MyClass',
Expand All @@ -253,9 +253,9 @@ describe('trace decorator', () => {
expect(response).toMatchObject({ data: 'Success' });
});

it('should async trace errors and pass correct traceHandlerMock and traceContext', async () => {
it('should async trace errors and pass correct onTraceEventMock and traceContext', async () => {
await expect(new MyClass().fetchDataFunction('invalid-url')).rejects.toThrow('Invalid URL provided.');
expect(traceHandlerFetchDataMock).toHaveBeenCalledWith(
expect(onTraceEventFetchDataMock).toHaveBeenCalledWith(
expect.objectContaining({
metadata: expect.objectContaining({
class: 'MyClass',
Expand All @@ -275,7 +275,7 @@ describe('trace decorator', () => {
it('should async trace successfully divisionFunctionOnAnotherDecorator', async () => {
const classInstance = new MyClass();
const response = classInstance.divisionFunctionOnAnotherDecorator(1, 2);
expect(traceHandlerDivisionMock).toHaveBeenNthCalledWith(
expect(onTraceEventDivisionMock).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
metadata: expect.objectContaining({
Expand All @@ -297,7 +297,7 @@ describe('trace decorator', () => {

it('should async trace error divisionFunctionOnAnotherDecorator', async () => {
expect(() => new MyClass().divisionFunctionOnAnotherDecorator(1, 0)).toThrow('Throwing because division by zero is not allowed.');
expect(traceHandlerDivisionMock).toHaveBeenNthCalledWith(
expect(onTraceEventDivisionMock).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
metadata: expect.objectContaining({
Expand All @@ -318,7 +318,7 @@ describe('trace decorator', () => {

it('should async trace successfully fetchDataFunctionOnAnotherDecorator', async () => {
const response = await new MyClass().fetchDataFunctionOnAnotherDecorator('https://api.example.com/data');
expect(traceHandlerFetchDataMock).toHaveBeenNthCalledWith(
expect(onTraceEventFetchDataMock).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
metadata: expect.objectContaining({
Expand All @@ -340,7 +340,7 @@ describe('trace decorator', () => {

it('should async trace error fetchDataFunctionOnAnotherDecorator', async () => {
await expect(new MyClass().fetchDataFunctionOnAnotherDecorator('invalid-url')).rejects.toThrow('Invalid URL provided.');
expect(traceHandlerFetchDataMock).toHaveBeenNthCalledWith(
expect(onTraceEventFetchDataMock).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
metadata: expect.objectContaining({
Expand All @@ -361,7 +361,7 @@ describe('trace decorator', () => {

it('should async trace error fetchDataFunctionOnAnotherDecorator cought', async () => {
const response = await new MyClass().fetchDataFunctionOnAnotherDecoratorCoughtError('invalid-url');
expect(traceHandlerFetchDataMock).toHaveBeenNthCalledWith(
expect(onTraceEventFetchDataMock).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
metadata: expect.objectContaining({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { isAsync } from '../common/utils/isAsync';
/**
* Method decorator to trace function execution, capturing metadata, inputs, outputs, and errors.
*
* @param traceHandler - handle function of the trace context.
* @param onTraceEvent - handle function of the trace context.
* @param additionalContext - Additional metadata to attach to the trace context.
* @param options - Configuration options:
* - `contextKey`: Key to store trace context on the instance.
Expand All @@ -14,7 +14,7 @@ import { isAsync } from '../common/utils/isAsync';
* @returns A method decorator that wraps the original function with execution tracing.
*/
export function trace<O>(
traceHandler: (traceContext: TraceContext<O>) => void,
onTraceEvent: (traceContext: TraceContext<O>) => void,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
additionalContext: Record<string, any> = {},
options: { contextKey?: string; errorStrategy?: 'catch' | 'throw' } = {
Expand All @@ -37,7 +37,7 @@ export function trace<O>(
originalMethod.bind(this),
args,
(traceContext) => {
return (traceHandler.bind(this) as typeof traceHandler)({ ...traceContext, ...thisTraceContext });
return (onTraceEvent.bind(this) as typeof onTraceEvent)({ ...traceContext, ...thisTraceContext });
},
options
)?.then((r) => (options?.errorStrategy === 'catch' ? r.errors : r.outputs));
Expand All @@ -46,7 +46,7 @@ export function trace<O>(
originalMethod.bind(this) as () => O,
args,
(traceContext) => {
return (traceHandler.bind(this) as typeof traceHandler)({ ...traceContext, ...thisTraceContext });
return (onTraceEvent.bind(this) as typeof onTraceEvent)({ ...traceContext, ...thisTraceContext });
},
options
);
Expand Down
Loading
Loading