diff --git a/src/lib/features/integration-events/integration-events-service.ts b/src/lib/features/integration-events/integration-events-service.ts new file mode 100644 index 0000000000..d41630ffb7 --- /dev/null +++ b/src/lib/features/integration-events/integration-events-service.ts @@ -0,0 +1,51 @@ +import type { Logger } from '../../logger'; +import type { IFlagResolver, IUnleashConfig } from '../../types'; +import type { + IntegrationEventsStore, + IntegrationEventWriteModel, +} from './integration-events-store'; +import type { IntegrationEventSchema } from '../../openapi/spec/integration-event-schema'; + +export class IntegrationEventsService { + private readonly logger: Logger; + private integrationEventsStore: IntegrationEventsStore; + private flagResolver: IFlagResolver; + + constructor( + { + integrationEventsStore, + }: { integrationEventsStore: IntegrationEventsStore }, + { + getLogger, + flagResolver, + }: Pick, + ) { + this.integrationEventsStore = integrationEventsStore; + this.flagResolver = flagResolver; + this.logger = getLogger('integration-events-service'); + } + + async getPaginatedEvents( + id: number, + limit: number, + offset: number, + ): Promise { + return this.integrationEventsStore.getPaginatedEvents( + id, + limit, + offset, + ); + } + + async registerEvent( + integrationEvent: IntegrationEventWriteModel, + ): Promise { + return this.integrationEventsStore.insert(integrationEvent); + } + + async cleanUpEvents(): Promise { + if (!this.flagResolver.isEnabled('integrationEvents')) return; + + await this.integrationEventsStore.cleanUpEvents(); + } +} diff --git a/src/lib/features/integration-events/integration-events.e2e.test.ts b/src/lib/features/integration-events/integration-events.e2e.test.ts index 0f12f35328..6c5f00a3ad 100644 --- a/src/lib/features/integration-events/integration-events.e2e.test.ts +++ b/src/lib/features/integration-events/integration-events.e2e.test.ts @@ -5,14 +5,12 @@ import { } from '../../../test/e2e/helpers/test-helper'; import getLogger from '../../../test/fixtures/no-logger'; import { TEST_AUDIT_USER } from '../../types'; -import type { - IntegrationEventsStore, - IntegrationEventWriteModel, -} from './integration-events-store'; +import type { IntegrationEventsService } from './integration-events-service'; +import type { IntegrationEventWriteModel } from './integration-events-store'; let app: IUnleashTest; let db: ITestDb; -let integrationEventsStore: IntegrationEventsStore; +let integrationEventsService: IntegrationEventsService; let integrationId: number; const EVENT_SUCCESS: IntegrationEventWriteModel = { @@ -50,7 +48,7 @@ beforeAll(async () => { }, db.rawDatabase, ); - integrationEventsStore = db.stores.integrationEventsStore; + integrationEventsService = app.services.integrationEventsService; }); beforeEach(async () => { @@ -80,7 +78,7 @@ const insertPastEvent = async ( event: IntegrationEventWriteModel, date: Date, ): Promise => { - const { id } = await integrationEventsStore.insert(event); + const { id } = await integrationEventsService.registerEvent(event); await db.rawDatabase.raw( `UPDATE integration_events SET created_at = ? WHERE id = ?`, @@ -99,10 +97,10 @@ const getTestEventFailed = () => ({ }); test('insert and fetch integration events', async () => { - await integrationEventsStore.insert(getTestEventSuccess()); - await integrationEventsStore.insert(getTestEventFailed()); + await integrationEventsService.registerEvent(getTestEventSuccess()); + await integrationEventsService.registerEvent(getTestEventFailed()); - const events = await integrationEventsStore.getPaginatedEvents( + const events = await integrationEventsService.getPaginatedEvents( integrationId, 10, 0, @@ -114,10 +112,10 @@ test('insert and fetch integration events', async () => { }); test('paginate to latest event', async () => { - await integrationEventsStore.insert(getTestEventSuccess()); - await integrationEventsStore.insert(getTestEventFailed()); + await integrationEventsService.registerEvent(getTestEventSuccess()); + await integrationEventsService.registerEvent(getTestEventFailed()); - const events = await integrationEventsStore.getPaginatedEvents( + const events = await integrationEventsService.getPaginatedEvents( integrationId, 1, 0, @@ -128,10 +126,10 @@ test('paginate to latest event', async () => { }); test('paginate to second most recent event', async () => { - await integrationEventsStore.insert(getTestEventSuccess()); - await integrationEventsStore.insert(getTestEventFailed()); + await integrationEventsService.registerEvent(getTestEventSuccess()); + await integrationEventsService.registerEvent(getTestEventFailed()); - const events = await integrationEventsStore.getPaginatedEvents( + const events = await integrationEventsService.getPaginatedEvents( integrationId, 1, 1, @@ -142,10 +140,10 @@ test('paginate to second most recent event', async () => { }); test('paginate to non-existing event, returning empty array', async () => { - await integrationEventsStore.insert(getTestEventSuccess()); - await integrationEventsStore.insert(getTestEventFailed()); + await integrationEventsService.registerEvent(getTestEventSuccess()); + await integrationEventsService.registerEvent(getTestEventFailed()); - const events = await integrationEventsStore.getPaginatedEvents( + const events = await integrationEventsService.getPaginatedEvents( integrationId, 1, 999, @@ -164,11 +162,11 @@ test('clean up events, keeping events from the last 2 hours', async () => { await insertPastEvent(getTestEventFailed(), twoDaysAgo); await insertPastEvent(getTestEventSuccess(), longTimeAgo); await insertPastEvent(getTestEventFailed(), oneHourAgo); - await integrationEventsStore.insert(getTestEventSuccess()); + await integrationEventsService.registerEvent(getTestEventSuccess()); - await integrationEventsStore.cleanUpEvents(); + await integrationEventsService.cleanUpEvents(); - const events = await integrationEventsStore.getPaginatedEvents( + const events = await integrationEventsService.getPaginatedEvents( integrationId, 10, 0, @@ -181,12 +179,12 @@ test('clean up events, keeping events from the last 2 hours', async () => { test('clean up events, keeping the last 100 events', async () => { for (let i = 0; i < 200; i++) { - await integrationEventsStore.insert(getTestEventSuccess()); + await integrationEventsService.registerEvent(getTestEventSuccess()); } - await integrationEventsStore.cleanUpEvents(); + await integrationEventsService.cleanUpEvents(); - const events = await integrationEventsStore.getPaginatedEvents( + const events = await integrationEventsService.getPaginatedEvents( integrationId, 200, 0, diff --git a/src/lib/features/scheduler/schedule-services.ts b/src/lib/features/scheduler/schedule-services.ts index a096c237a9..95e03c114b 100644 --- a/src/lib/features/scheduler/schedule-services.ts +++ b/src/lib/features/scheduler/schedule-services.ts @@ -31,6 +31,7 @@ export const scheduleServices = async ( lastSeenService, frontendApiService, clientMetricsServiceV2, + integrationEventsService, } = services; schedulerService.schedule( @@ -165,4 +166,10 @@ export const scheduleServices = async ( 'setFeatureCreatedByUserIdFromEvents', ); } + + schedulerService.schedule( + integrationEventsService.cleanUpEvents.bind(integrationEventsService), + minutesToMilliseconds(15), + 'cleanUpIntegrationEvents', + ); }; diff --git a/src/lib/services/index.ts b/src/lib/services/index.ts index 58a0328fc6..8a4f04ef60 100644 --- a/src/lib/services/index.ts +++ b/src/lib/services/index.ts @@ -134,6 +134,7 @@ import { createApiTokenService, createFakeApiTokenService, } from '../features/api-tokens/createApiTokenService'; +import { IntegrationEventsService } from '../features/integration-events/integration-events-service'; export const createServices = ( stores: IUnleashStores, @@ -191,6 +192,10 @@ export const createServices = ( ? withTransactional(createTagTypeService(config), db) : withFakeTransactional(createFakeTagTypeService(config)); const tagTypeService = transactionalTagTypeService; + const integrationEventsService = new IntegrationEventsService( + stores, + config, + ); const addonService = new AddonService( stores, config, @@ -436,6 +441,7 @@ export const createServices = ( jobService, featureLifecycleService, transactionalFeatureLifecycleService, + integrationEventsService, }; }; @@ -484,4 +490,5 @@ export { ProjectInsightsService, JobService, FeatureLifecycleService, + IntegrationEventsService, }; diff --git a/src/lib/types/services.ts b/src/lib/types/services.ts index bf302f2780..1b83349f65 100644 --- a/src/lib/types/services.ts +++ b/src/lib/types/services.ts @@ -54,6 +54,7 @@ import type { InactiveUsersService } from '../users/inactive/inactive-users-serv import type { ProjectInsightsService } from '../features/project-insights/project-insights-service'; import type { JobService } from '../features/scheduler/job-service'; import type { FeatureLifecycleService } from '../features/feature-lifecycle/feature-lifecycle-service'; +import type { IntegrationEventsService } from '../features/integration-events/integration-events-service'; export interface IUnleashServices { accessService: AccessService; @@ -118,4 +119,5 @@ export interface IUnleashServices { jobService: JobService; featureLifecycleService: FeatureLifecycleService; transactionalFeatureLifecycleService: WithTransactional; + integrationEventsService: IntegrationEventsService; }