Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GRAFX-3507 Support multiple event handlers #482

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
842c4aa
Add eventsubscriptions to SDK
pietervp Jul 11, 2024
2056edf
formatting + eslint
pietervp Jul 11, 2024
c52c2e8
chore: Update EventSubscription class to use `any` instead of `unknow…
pietervp Jul 11, 2024
78cfc5c
Refactor EventSubscription class to use trigger instead of executeCal…
pietervp Jul 12, 2024
55032ad
Add SingleSubscription test case to EventSubscription.test.ts
pietervp Jul 12, 2024
2699bb8
Address PR remarks
pietervp Jul 17, 2024
3fea861
Refactor logging configuration in BaseConfigType
pietervp Jul 17, 2024
c4dbf79
Refactor logging configuration and update log level enums in CommonTy…
pietervp Jul 17, 2024
64b72c3
Add EngineEventBase and EngineEvent classes to SDK
pietervp Jul 17, 2024
65b57fe
Refactor EngineCallbackHandler to handle async callbacks and errors
pietervp Jul 17, 2024
76987e8
Merge branch 'main' into feature/GRAFX-3507-multiple-event-handlers
pietervp Jul 17, 2024
acd965c
Merge branch 'main' into feature/GRAFX-3507-multiple-event-handlers
psamusev Dec 13, 2024
e49b95e
Merge branch 'main' into feature/GRAFX-3507-multiple-event-handlers
psamusev Dec 16, 2024
5f2f6f3
chore: adapt to latest main
psamusev Dec 16, 2024
c0e930c
chore: fixed tests
psamusev Dec 16, 2024
da0566c
chore: fix next subscribers
psamusev Dec 16, 2024
990da2e
chore: fix tests
psamusev Dec 16, 2024
667c4fa
chore: extract error handling to separate function
psamusev Dec 16, 2024
87b74a0
Merge branch 'main' into feature/GRAFX-3507-multiple-event-handlers
psamusev Dec 23, 2024
e4c0764
Merge branch 'main' into feature/GRAFX-3507-multiple-event-handlers
psamusev Dec 30, 2024
65a5dca
chore: update types
psamusev Dec 30, 2024
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
Prev Previous commit
Next Next commit
Refactor logging configuration in BaseConfigType
  • Loading branch information
pietervp committed Jul 17, 2024
commit 3fea861d720e5150fd05ae4880a70103e3e6da43
153 changes: 85 additions & 68 deletions packages/sdk/src/tests/utils/EventSubscription.test.ts
Original file line number Diff line number Diff line change
@@ -124,82 +124,99 @@ describe('EventHelper', () => {
});
});


describe('EventSubscription Error Handling', () => {
let mockLogger: jest.Mock;
let legacyEventHandler: jest.Mock;

beforeEach(() => {
mockLogger = jest.fn();
legacyEventHandler = jest.fn();
mockLogger = jest.fn();
legacyEventHandler = jest.fn();
});

it('logs an error when a callback errors out with LOG behavior', () => {
const eventSubscription = new EventSubscription(() => undefined, mockLogger);
const errorCallback = jest.fn(() => { throw new Error('Test Error'); });

eventSubscription.registerCallback(errorCallback, CallbackErrorBehavior.LOG);

eventSubscription.trigger();

expect(mockLogger).toHaveBeenCalledWith(LogLevel.ERROR, LogCategory.EVENT, expect.stringContaining('Error in callback'));
expect(errorCallback).toHaveBeenCalled();
});

const eventSubscription = new EventSubscription(() => undefined, mockLogger);
const errorCallback = jest.fn(() => {
throw new Error('Test Error');
});

eventSubscription.registerCallback(errorCallback, CallbackErrorBehavior.LOG);

eventSubscription.trigger();

expect(mockLogger).toHaveBeenCalledWith(
LogLevel.ERROR,
LogCategory.EVENT,
expect.stringContaining('Error in callback'),
);
expect(errorCallback).toHaveBeenCalled();
});

it('throws an error when a callback errors out with THROW behavior', () => {
const eventSubscription = new EventSubscription(() => undefined, mockLogger);
const errorCallback = jest.fn(() => { throw new Error('Test Error'); });

eventSubscription.registerCallback(errorCallback, CallbackErrorBehavior.THROW);

expect(() => eventSubscription.trigger()).toThrow('Test Error');
expect(errorCallback).toHaveBeenCalled();
});

const eventSubscription = new EventSubscription(() => undefined, mockLogger);
const errorCallback = jest.fn(() => {
throw new Error('Test Error');
});

eventSubscription.registerCallback(errorCallback, CallbackErrorBehavior.THROW);

expect(() => eventSubscription.trigger()).toThrow('Test Error');
expect(errorCallback).toHaveBeenCalled();
});

it('removes the callback when a callback errors out with REMOVE behavior', () => {
const eventSubscription = new EventSubscription(() => undefined, mockLogger);
const errorCallback = jest.fn(() => { throw new Error('Test Error'); });

const callbackName = eventSubscription.registerCallback(errorCallback, CallbackErrorBehavior.REMOVE);

eventSubscription.trigger();

expect(mockLogger).toHaveBeenCalledWith(LogLevel.WARN, LogCategory.EVENT, expect.stringContaining('Removed callback'));
expect(errorCallback).toHaveBeenCalled();
expect(() => eventSubscription.trigger()).not.toThrow();
});

const eventSubscription = new EventSubscription(() => undefined, mockLogger);
const errorCallback = jest.fn(() => {
throw new Error('Test Error');
});

eventSubscription.registerCallback(errorCallback, CallbackErrorBehavior.REMOVE);

eventSubscription.trigger();

expect(mockLogger).toHaveBeenCalledWith(
LogLevel.WARN,
LogCategory.EVENT,
expect.stringContaining('Removed callback'),
);
expect(errorCallback).toHaveBeenCalled();
expect(() => eventSubscription.trigger()).not.toThrow();
});

it('executes legacy event handler if provided', () => {
const legacyCallback = jest.fn();
legacyEventHandler.mockReturnValue(legacyCallback);
const eventSubscription = new EventSubscription(legacyEventHandler, mockLogger);
eventSubscription.trigger();
expect(legacyEventHandler).toHaveBeenCalled();
expect(legacyCallback).toHaveBeenCalled();
});
const legacyCallback = jest.fn();
legacyEventHandler.mockReturnValue(legacyCallback);

const eventSubscription = new EventSubscription(legacyEventHandler, mockLogger);

eventSubscription.trigger();

expect(legacyEventHandler).toHaveBeenCalled();
expect(legacyCallback).toHaveBeenCalled();
});

it('does not log if no logger provided for LOG behavior', () => {
const eventSubscription = new EventSubscription(() => undefined);
const errorCallback = jest.fn(() => { throw new Error('Test Error'); });

eventSubscription.registerCallback(errorCallback, CallbackErrorBehavior.LOG);

eventSubscription.trigger();

expect(mockLogger).not.toHaveBeenCalled();
});

const eventSubscription = new EventSubscription(() => undefined);
const errorCallback = jest.fn(() => {
throw new Error('Test Error');
});

eventSubscription.registerCallback(errorCallback, CallbackErrorBehavior.LOG);

eventSubscription.trigger();

expect(mockLogger).not.toHaveBeenCalled();
});

it('does not log if no logger provided for REMOVE behavior', () => {
const eventSubscription = new EventSubscription(() => undefined);
const errorCallback = jest.fn(() => { throw new Error('Test Error'); });

eventSubscription.registerCallback(errorCallback, CallbackErrorBehavior.REMOVE);

eventSubscription.trigger();

expect(mockLogger).not.toHaveBeenCalled();
});
});
const eventSubscription = new EventSubscription(() => undefined);
const errorCallback = jest.fn(() => {
throw new Error('Test Error');
});

eventSubscription.registerCallback(errorCallback, CallbackErrorBehavior.REMOVE);

eventSubscription.trigger();

expect(mockLogger).not.toHaveBeenCalled();
});
});
115 changes: 115 additions & 0 deletions packages/sdk/src/tests/utils/Logging.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { LogLevel, RuntimeConfigType, ConfigType, LoggerFunction, LogCategory } from '../../types/CommonTypes';
import { ConfigHelper } from '../../utils/ConfigHelper';

describe('ConfigHelper', () => {
let originalConsoleError: unknown;
let originalConsoleWarn: unknown;
let originalConsoleLog: unknown;

beforeEach(() => {
originalConsoleError = console.error;
originalConsoleWarn = console.warn;
originalConsoleLog = console.log;

console.error = jest.fn();
console.warn = jest.fn();
console.log = jest.fn();
});

afterEach(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
console.error = originalConsoleError;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
console.warn = originalConsoleWarn;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
console.log = originalConsoleLog;
});

const createConfigWithLogLevel = (logLevel: LogLevel): RuntimeConfigType => {
const config: ConfigType = {
logging: {
logLevel: logLevel,
},
};
return ConfigHelper.createRuntimeConfig(config);
};

it('should log error messages when log level is ERROR', () => {
const runtimeConfig = createConfigWithLogLevel(LogLevel.ERROR);
getLogger(runtimeConfig)(LogLevel.ERROR, LogCategory.GENERAL, 'This is an error message');
getLogger(runtimeConfig)(LogLevel.WARN, LogCategory.GENERAL, 'This is a warning message');
getLogger(runtimeConfig)(LogLevel.INFO, LogCategory.GENERAL, 'This is an info message');

expect(console.error).toHaveBeenCalledWith('[general] [error] This is an error message');
expect(console.warn).not.toHaveBeenCalled();
expect(console.log).not.toHaveBeenCalled();
});

it('should log warning and error messages when log level is WARN', () => {
const runtimeConfig = createConfigWithLogLevel(LogLevel.WARN);
getLogger(runtimeConfig)(LogLevel.ERROR, LogCategory.GENERAL, 'This is an error message');
getLogger(runtimeConfig)(LogLevel.WARN, LogCategory.GENERAL, 'This is a warning message');
getLogger(runtimeConfig)(LogLevel.INFO, LogCategory.GENERAL, 'This is an info message');

expect(console.error).toHaveBeenCalledWith('[general] [error] This is an error message');
expect(console.warn).toHaveBeenCalledWith('[general] [warn] This is a warning message');
expect(console.log).not.toHaveBeenCalled();
});

it('should log info, warning, and error messages when log level is INFO', () => {
const runtimeConfig = createConfigWithLogLevel(LogLevel.INFO);
getLogger(runtimeConfig)(LogLevel.ERROR, LogCategory.GENERAL, 'This is an error message');
getLogger(runtimeConfig)(LogLevel.WARN, LogCategory.GENERAL, 'This is a warning message');
getLogger(runtimeConfig)(LogLevel.INFO, LogCategory.GENERAL, 'This is an info message');

expect(console.error).toHaveBeenCalledWith('[general] [error] This is an error message');
expect(console.warn).toHaveBeenCalledWith('[general] [warn] This is a warning message');
expect(console.log).toHaveBeenCalledWith('[general] [info] This is an info message');
});

it('should log only ERROR when loglevel is undefined', () => {
const runtimeConfig = createConfigWithLogLevel(undefined as unknown as LogLevel);
getLogger(runtimeConfig)(LogLevel.ERROR, LogCategory.GENERAL, 'This is an error message');
getLogger(runtimeConfig)(LogLevel.WARN, LogCategory.GENERAL, 'This is a warning message');
getLogger(runtimeConfig)(LogLevel.INFO, LogCategory.GENERAL, 'This is an info message');

expect(console.error).toHaveBeenCalledTimes(1);
expect(console.warn).not.toHaveBeenCalled();
expect(console.log).not.toHaveBeenCalled();
});

it('should adopt functionality when loglevel changes', () => {
const runtimeConfig = createConfigWithLogLevel(LogLevel.ERROR);
getLogger(runtimeConfig)(LogLevel.ERROR, LogCategory.GENERAL, 'This is an error message');
getLogger(runtimeConfig)(LogLevel.WARN, LogCategory.GENERAL, 'This is a warning message');
getLogger(runtimeConfig)(LogLevel.INFO, LogCategory.GENERAL, 'This is an info message');

expect(console.error).toHaveBeenCalledWith('[general] [error] This is an error message');
expect(console.warn).not.toHaveBeenCalled();
expect(console.log).not.toHaveBeenCalled();

if (runtimeConfig.logging) {
runtimeConfig.logging.logLevel = LogLevel.WARN;
} else {
throw new Error('runtimeConfig.logging is undefined');
}
getLogger(runtimeConfig)(LogLevel.ERROR, LogCategory.GENERAL, 'This is an error message');
getLogger(runtimeConfig)(LogLevel.WARN, LogCategory.GENERAL, 'This is a warning message');
getLogger(runtimeConfig)(LogLevel.INFO, LogCategory.GENERAL, 'This is an info message');

expect(console.error).toHaveBeenCalledTimes(2);
expect(console.warn).toHaveBeenCalledTimes(1);
expect(console.log).not.toHaveBeenCalled();
});
});

function getLogger(runtimeConfig?: RuntimeConfigType): LoggerFunction {
if (runtimeConfig?.logging?.logger === undefined) {
throw new Error('Logger is undefined');
}

return runtimeConfig.logging.logger;
}
6 changes: 5 additions & 1 deletion packages/sdk/src/types/CommonTypes.ts
Original file line number Diff line number Diff line change
@@ -29,7 +29,10 @@ export type BaseConfigType = {
enableNextSubscribers?: {
onVariableListChanged: boolean;
};
logger?: LoggerFunction;
logging?: {
logLevel?: LogLevel;
logger?: LoggerFunction;
};
};

export type LoggerFunction = (logLevel: LogLevel, category: LogCategory, message: string) => void;
@@ -44,6 +47,7 @@ export enum LogCategory {
GENERAL = 'general',
CONNECTOR = 'connector',
EVENT = 'event',
ENGINE = 'engine',
}

/**
Loading