Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
6 changes: 3 additions & 3 deletions lib/core/decision_service/index.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ import {
DECISION_SOURCES,
} from '../../utils/enums';
import { createLogger } from '../../plugins/logger';
import { createForwardingEventProcessor } from '../../plugins/event_processor/forwarding_event_processor';
import { getForwardingEventProcessor } from '../../event_processor/forwarding_event_processor';
import { createNotificationCenter } from '../notification_center';
import Optimizely from '../../optimizely';
import OptimizelyUserContext from '../../optimizely_user_context';
import projectConfig, { createProjectConfig } from '../../project_config/project_config';
import AudienceEvaluator from '../audience_evaluator';
import errorHandler from '../../plugins/error_handler';
import eventDispatcher from '../../plugins/event_dispatcher/index.node';
import eventDispatcher from '../../event_processor/default_dispatcher.browser';
import * as jsonSchemaValidator from '../../utils/json_schema_validator';
import { getMockProjectConfigManager } from '../../tests/mock/mock_project_config_manager';

Expand Down Expand Up @@ -1075,7 +1075,7 @@ describe('lib/core/decision_service', function() {
jsonSchemaValidator: jsonSchemaValidator,
isValidInstance: true,
logger: createdLogger,
eventProcessor: createForwardingEventProcessor(eventDispatcher),
eventProcessor: getForwardingEventProcessor(eventDispatcher),
notificationCenter: createNotificationCenter(createdLogger, errorHandler),
errorHandler: errorHandler,
});
Expand Down
2 changes: 1 addition & 1 deletion lib/core/event_builder/build_event_v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
EventTags,
ConversionEvent,
ImpressionEvent,
} from '../../modules/event_processor';
} from '../../event_processor';

import { Event } from '../../shared_types';

Expand Down
2 changes: 1 addition & 1 deletion lib/core/event_builder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/
import { LoggerFacade } from '../../modules/logging';
import { EventV1 as CommonEventParams } from '../../modules/event_processor';
import { EventV1 as CommonEventParams } from '../../event_processor';

import fns from '../../utils/fns';
import { CONTROL_ATTRIBUTES, RESERVED_EVENT_KEYWORDS } from '../../utils/enums';
Expand Down
33 changes: 33 additions & 0 deletions lib/event_processor/default_dispatcher.browser.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { vi, expect, it, describe, afterAll } from 'vitest';

vi.mock('./default_dispatcher', () => {
const DefaultEventDispatcher = vi.fn();
return { DefaultEventDispatcher };
});

vi.mock('../utils/http_request_handler/browser_request_handler', () => {
const BrowserRequestHandler = vi.fn();
return { BrowserRequestHandler };
});

import { DefaultEventDispatcher } from './default_dispatcher';
import { BrowserRequestHandler } from '../utils/http_request_handler/browser_request_handler';
import eventDispatcher from './default_dispatcher.browser';

describe('eventDispatcher', () => {
afterAll(() => {
MockDefaultEventDispatcher.mockReset();
MockBrowserRequestHandler.mockReset();
});
const MockBrowserRequestHandler = vi.mocked(BrowserRequestHandler);
const MockDefaultEventDispatcher = vi.mocked(DefaultEventDispatcher);

it('creates and returns the instance by calling DefaultEventDispatcher', () => {
expect(Object.is(eventDispatcher, MockDefaultEventDispatcher.mock.instances[0])).toBe(true);
});

it('uses a BrowserRequestHandler', () => {
expect(Object.is(eventDispatcher, MockDefaultEventDispatcher.mock.instances[0])).toBe(true);
expect(Object.is(MockDefaultEventDispatcher.mock.calls[0][0], MockBrowserRequestHandler.mock.instances[0])).toBe(true);
});
});
23 changes: 23 additions & 0 deletions lib/event_processor/default_dispatcher.browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright 2016-2017, 2020-2022, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { BrowserRequestHandler } from "../utils/http_request_handler/browser_request_handler";
import { EventDispatcher } from '../event_processor';
import { DefaultEventDispatcher } from './default_dispatcher';

const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new BrowserRequestHandler());

export default eventDispatcher;
34 changes: 34 additions & 0 deletions lib/event_processor/default_dispatcher.node.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { vi, expect, it, describe, afterAll } from 'vitest';

vi.mock('./default_dispatcher', () => {
const DefaultEventDispatcher = vi.fn();
return { DefaultEventDispatcher };
});

vi.mock('../utils/http_request_handler/node_request_handler', () => {
const NodeRequestHandler = vi.fn();
return { NodeRequestHandler };
});

import { DefaultEventDispatcher } from './default_dispatcher';
import { NodeRequestHandler } from '../utils/http_request_handler/node_request_handler';
import eventDispatcher from './default_dispatcher.node';

describe('eventDispatcher', () => {
const MockNodeRequestHandler = vi.mocked(NodeRequestHandler);
const MockDefaultEventDispatcher = vi.mocked(DefaultEventDispatcher);

afterAll(() => {
MockDefaultEventDispatcher.mockReset();
MockNodeRequestHandler.mockReset();
})

it('creates and returns the instance by calling DefaultEventDispatcher', () => {
expect(Object.is(eventDispatcher, MockDefaultEventDispatcher.mock.instances[0])).toBe(true);
});

it('uses a NodeRequestHandler', () => {
expect(Object.is(eventDispatcher, MockDefaultEventDispatcher.mock.instances[0])).toBe(true);
expect(Object.is(MockDefaultEventDispatcher.mock.calls[0][0], MockNodeRequestHandler.mock.instances[0])).toBe(true);
});
});
22 changes: 22 additions & 0 deletions lib/event_processor/default_dispatcher.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright 2016-2018, 2020-2021, 2024 Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EventDispatcher } from '../event_processor';
import { NodeRequestHandler } from '../utils/http_request_handler/node_request_handler';
import { DefaultEventDispatcher } from './default_dispatcher';

const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new NodeRequestHandler());

export default eventDispatcher;
102 changes: 102 additions & 0 deletions lib/event_processor/default_dispatcher.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { expect, vi, describe, it } from 'vitest';
import { DefaultEventDispatcher } from './default_dispatcher';
import { EventV1 } from '../event_processor';

const getEvent = (): EventV1 => {
return {
account_id: 'string',
project_id: 'string',
revision: 'string',
client_name: 'string',
client_version: 'string',
anonymize_ip: true,
enrich_decisions: false,
visitors: [],
};
};

describe('DefaultEventDispatcher', () => {
it('reject the response promise if the eventObj.httpVerb is not POST', async () => {
const eventObj = {
url: 'https://cdn.com/event',
params: getEvent(),
httpVerb: 'GET' as const,
};

const requestHnadler = {
makeRequest: vi.fn().mockReturnValue({
abort: vi.fn(),
responsePromise: Promise.resolve({ statusCode: 203 }),
}),
};

const dispatcher = new DefaultEventDispatcher(requestHnadler);
await expect(dispatcher.dispatchEvent(eventObj)).rejects.toThrow();
});

it('sends correct headers and data to the requestHandler', async () => {
const eventObj = {
url: 'https://cdn.com/event',
params: getEvent(),
httpVerb: 'POST' as const,
};

const requestHnadler = {
makeRequest: vi.fn().mockReturnValue({
abort: vi.fn(),
responsePromise: Promise.resolve({ statusCode: 203 }),
}),
};

const dispatcher = new DefaultEventDispatcher(requestHnadler);
await dispatcher.dispatchEvent(eventObj);

expect(requestHnadler.makeRequest).toHaveBeenCalledWith(
eventObj.url,
{
'content-type': 'application/json',
'content-length': JSON.stringify(eventObj.params).length.toString(),
},
'POST',
JSON.stringify(eventObj.params)
);
});

it('returns a promise that resolves with correct value if the response of the requestHandler resolves', async () => {
const eventObj = {
url: 'https://cdn.com/event',
params: getEvent(),
httpVerb: 'POST' as const,
};

const requestHnadler = {
makeRequest: vi.fn().mockReturnValue({
abort: vi.fn(),
responsePromise: Promise.resolve({ statusCode: 203 }),
}),
};

const dispatcher = new DefaultEventDispatcher(requestHnadler);
const response = await dispatcher.dispatchEvent(eventObj);

expect(response.statusCode).toEqual(203);
});

it('returns a promise that rejects if the response of the requestHandler rejects', async () => {
const eventObj = {
url: 'https://cdn.com/event',
params: getEvent(),
httpVerb: 'POST' as const,
};

const requestHnadler = {
makeRequest: vi.fn().mockReturnValue({
abort: vi.fn(),
responsePromise: Promise.reject(new Error('error')),
}),
};

const dispatcher = new DefaultEventDispatcher(requestHnadler);
await expect(dispatcher.dispatchEvent(eventObj)).rejects.toThrow();
});
});
30 changes: 30 additions & 0 deletions lib/event_processor/default_dispatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

import { RequestHandler } from '../utils/http_request_handler/http';
import { EventDispatcher, EventDispatcherResponse, EventV1Request } from '../event_processor';

export class DefaultEventDispatcher implements EventDispatcher {
private requestHandler: RequestHandler;

constructor(requestHandler: RequestHandler) {
this.requestHandler = requestHandler;
}

async dispatchEvent(
eventObj: EventV1Request
): Promise<EventDispatcherResponse> {
// Non-POST requests not supported
if (eventObj.httpVerb !== 'POST') {
return Promise.reject(new Error('Only POST requests are supported'));
}

const dataString = JSON.stringify(eventObj.params);

const headers = {
'content-type': 'application/json',
'content-length': dataString.length.toString(),
};

const abortableRequest = this.requestHandler.makeRequest(eventObj.url, headers, 'POST', dataString);
return abortableRequest.responsePromise;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@
import { EventV1 } from "./v1/buildEventV1";

export type EventDispatcherResponse = {
statusCode: number
statusCode?: number
}

export type EventDispatcherCallback = (response: EventDispatcherResponse) => void

export interface EventDispatcher {
dispatchEvent(event: EventV1Request, callback: EventDispatcherCallback): void
dispatchEvent(event: EventV1Request): Promise<EventDispatcherResponse>
}

export interface EventV1Request {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import { Managed } from './managed'
import { ConversionEvent, ImpressionEvent } from './events'
import { EventV1Request } from './eventDispatcher'
import { EventQueue, DefaultEventQueue, SingleEventQueue, EventQueueSink } from './eventQueue'
import { getLogger } from '../logging'
import { NOTIFICATION_TYPES } from '../../utils/enums'
import { NotificationSender } from '../../core/notification_center'
import { getLogger } from '../modules/logging'
import { NOTIFICATION_TYPES } from '../utils/enums'
import { NotificationSender } from '../core/notification_center'

export const DEFAULT_FLUSH_INTERVAL = 30000 // Unit is ms - default flush interval is 30s
export const DEFAULT_BATCH_SIZE = 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { getLogger } from '../logging';
import { getLogger } from '../modules/logging';
// TODO change this to use Managed from js-sdk-models when available
import { Managed } from './managed';

Expand Down
37 changes: 37 additions & 0 deletions lib/event_processor/event_processor_factory.browser.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';

vi.mock('./default_dispatcher.browser', () => {
return { default: {} };
});

vi.mock('./forwarding_event_processor', () => {
const getForwardingEventProcessor = vi.fn().mockReturnValue({});
return { getForwardingEventProcessor };
});

import { createForwardingEventProcessor } from './event_processor_factory.browser';
import { getForwardingEventProcessor } from './forwarding_event_processor';
import browserDefaultEventDispatcher from './default_dispatcher.browser';

describe('createForwardingEventProcessor', () => {
const mockGetForwardingEventProcessor = vi.mocked(getForwardingEventProcessor);

beforeEach(() => {
mockGetForwardingEventProcessor.mockClear();
});

it('returns forwarding event processor by calling getForwardingEventProcessor with the provided dispatcher', () => {
const eventDispatcher = {
dispatchEvent: vi.fn(),
};
const processor = createForwardingEventProcessor(eventDispatcher);
expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true);
expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, eventDispatcher);
});

it('uses the browser default event dispatcher if none is provided', () => {
const processor = createForwardingEventProcessor();
expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true);
expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, browserDefaultEventDispatcher);
});
});
13 changes: 13 additions & 0 deletions lib/event_processor/event_processor_factory.browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { getForwardingEventProcessor } from './forwarding_event_processor';
import { EventDispatcher } from './eventDispatcher';
import { EventProcessor } from './eventProcessor';
import defaultEventDispatcher from './default_dispatcher.browser';

export const createForwardingEventProcessor = (
eventDispatcher?: EventDispatcher,
): EventProcessor => {
if (!eventDispatcher) {
eventDispatcher = defaultEventDispatcher;
}
return getForwardingEventProcessor(eventDispatcher);
};
Loading