From b91df61994592aeff7ee8739c8e8fad85e560ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Fournier?= Date: Thu, 18 Jan 2024 13:15:21 +0100 Subject: [PATCH] chore: re use extract user methods (#5947) ## About the changes 1. Re-use existing methods in extract-user.ts: https://github.com/Unleash/unleash/blob/70f6a07f2c2ad1c39ee7bcc1d4cf35d39c285b67/src/lib/features/events/event-service.ts#L93-L101 2. Move event-service and event-store to features/event 3. Add export default in previous paths for backward compatibility: https://github.com/Unleash/unleash/blob/70f6a07f2c2ad1c39ee7bcc1d4cf35d39c285b67/src/lib/services/event-service.ts#L1-L4 and https://github.com/Unleash/unleash/blob/70f6a07f2c2ad1c39ee7bcc1d4cf35d39c285b67/src/lib/db/event-store.ts#L1-L4 --- src/lib/db/event-store.ts | 435 +----------------- src/lib/db/index.ts | 2 +- .../features/events/createEventsService.ts | 2 +- .../events}/event-service.test.ts | 8 +- src/lib/features/events/event-service.ts | 137 ++++++ .../events}/event-store.test.ts | 4 +- src/lib/features/events/event-store.ts | 433 +++++++++++++++++ .../createExportImportService.ts | 2 +- .../feature-toggle/feature-toggle-service.ts | 2 +- .../maintenance/maintenance-service.test.ts | 2 +- .../environment-service.ts | 2 +- .../features/project/createProjectService.ts | 2 +- .../scheduler/scheduler-service.test.ts | 2 +- src/lib/features/tag-type/tag-type-service.ts | 2 +- src/lib/routes/admin-api/event.ts | 2 +- src/lib/services/access-service.ts | 2 +- src/lib/services/addon-service.test.ts | 2 +- src/lib/services/addon-service.ts | 2 +- src/lib/services/api-token-service.test.ts | 2 +- src/lib/services/api-token-service.ts | 2 +- src/lib/services/context-service.ts | 2 +- src/lib/services/event-service.ts | 145 +----- src/lib/services/favorites-service.ts | 2 +- .../feature-service-potentially-stale.test.ts | 2 +- src/lib/services/feature-tag-service.ts | 2 +- src/lib/services/feature-type-service.ts | 2 +- src/lib/services/group-service.ts | 2 +- src/lib/services/index.ts | 2 +- src/lib/services/pat-service.ts | 2 +- src/lib/services/project-service.ts | 2 +- .../services/public-signup-token-service.ts | 2 +- src/lib/services/scheduler-service.test.ts | 2 +- src/lib/services/segment-service.ts | 2 +- src/lib/services/setting-service.ts | 2 +- src/lib/services/state-service.test.ts | 2 +- src/lib/services/state-service.ts | 2 +- src/lib/services/strategy-service.ts | 2 +- src/lib/services/tag-service.ts | 2 +- src/lib/services/user-service.test.ts | 2 +- src/lib/services/user-service.ts | 2 +- src/lib/types/services.ts | 2 +- src/lib/types/stores/event-store.ts | 2 +- src/test/fixtures/fake-event-store.ts | 2 +- 43 files changed, 620 insertions(+), 616 deletions(-) rename src/lib/{services => features/events}/event-service.test.ts (72%) create mode 100644 src/lib/features/events/event-service.ts rename src/lib/{db => features/events}/event-store.test.ts (93%) create mode 100644 src/lib/features/events/event-store.ts diff --git a/src/lib/db/event-store.ts b/src/lib/db/event-store.ts index d3132efe3a..074d9b30ed 100644 --- a/src/lib/db/event-store.ts +++ b/src/lib/db/event-store.ts @@ -1,433 +1,4 @@ -import { - IEvent, - IBaseEvent, - SEGMENT_UPDATED, - FEATURE_IMPORT, - FEATURES_IMPORTED, - IEventType, -} from '../types/events'; -import { LogProvider, Logger } from '../logger'; -import { IEventStore } from '../types/stores/event-store'; -import { ITag } from '../types/model'; -import { SearchEventsSchema } from '../openapi/spec/search-events-schema'; -import { sharedEventEmitter } from '../util/anyEventEmitter'; -import { Db } from './db'; -import { Knex } from 'knex'; -import EventEmitter from 'events'; - -const EVENT_COLUMNS = [ - 'id', - 'type', - 'created_by', - 'created_at', - 'created_by_user_id', - 'data', - 'pre_data', - 'tags', - 'feature_name', - 'project', - 'environment', -] as const; - -export type IQueryOperations = - | IWhereOperation - | IBeforeDateOperation - | IBetweenDatesOperation - | IForFeaturesOperation; - -interface IWhereOperation { - op: 'where'; - parameters: { - [key: string]: string; - }; -} - -interface IBeforeDateOperation { - op: 'beforeDate'; - parameters: { - dateAccessor: string; - date: string; - }; -} - -interface IBetweenDatesOperation { - op: 'betweenDate'; - parameters: { - dateAccessor: string; - range: string[]; - }; -} - -interface IForFeaturesOperation { - op: 'forFeatures'; - parameters: IForFeaturesParams; -} - -interface IForFeaturesParams { - type: string; - projectId: string; - environments: string[]; - features: string[]; -} - -export interface IEventTable { - id: number; - type: string; - created_by: string; - created_at: Date; - created_by_user_id: number; - data?: any; - pre_data?: any; - feature_name?: string; - project?: string; - environment?: string; - tags: ITag[]; -} - -const TABLE = 'events'; - -class EventStore implements IEventStore { - private db: Db; - - // only one shared event emitter should exist across all event store instances - private eventEmitter: EventEmitter = sharedEventEmitter; - - private logger: Logger; - - // a new DB has to be injected per transaction - constructor(db: Db, getLogger: LogProvider) { - this.db = db; - this.logger = getLogger('lib/db/event-store.ts'); - } - - async store(event: IBaseEvent): Promise { - try { - await this.db(TABLE) - .insert(this.eventToDbRow(event)) - .returning(EVENT_COLUMNS); - } catch (error: unknown) { - this.logger.warn(`Failed to store "${event.type}" event: ${error}`); - } - } - - async count(): Promise { - const count = await this.db(TABLE) - .count>() - .first(); - if (!count) { - return 0; - } - if (typeof count.count === 'string') { - return parseInt(count.count, 10); - } else { - return count.count; - } - } - - async filteredCount(eventSearch: SearchEventsSchema): Promise { - let query = this.db(TABLE); - if (eventSearch.type) { - query = query.andWhere({ type: eventSearch.type }); - } - if (eventSearch.project) { - query = query.andWhere({ project: eventSearch.project }); - } - if (eventSearch.feature) { - query = query.andWhere({ feature_name: eventSearch.feature }); - } - const count = await query.count().first(); - if (!count) { - return 0; - } - if (typeof count.count === 'string') { - return parseInt(count.count, 10); - } else { - return count.count; - } - } - - async batchStore(events: IBaseEvent[]): Promise { - try { - await this.db(TABLE).insert(events.map(this.eventToDbRow)); - } catch (error: unknown) { - this.logger.warn(`Failed to store events: ${error}`); - } - } - - async getMaxRevisionId(largerThan: number = 0): Promise { - const row = await this.db(TABLE) - .max('id') - .where((builder) => - builder - .whereNotNull('feature_name') - .orWhereIn('type', [ - SEGMENT_UPDATED, - FEATURE_IMPORT, - FEATURES_IMPORTED, - ]), - ) - .andWhere('id', '>=', largerThan) - .first(); - return row?.max ?? 0; - } - - async delete(key: number): Promise { - await this.db(TABLE).where({ id: key }).del(); - } - - async deleteAll(): Promise { - await this.db(TABLE).del(); - } - - destroy(): void {} - - async exists(key: number): Promise { - const result = await this.db.raw( - `SELECT EXISTS (SELECT 1 FROM ${TABLE} WHERE id = ?) AS present`, - [key], - ); - const { present } = result.rows[0]; - return present; - } - - async query(operations: IQueryOperations[]): Promise { - try { - let query: Knex.QueryBuilder = this.select(); - - operations.forEach((operation) => { - if (operation.op === 'where') { - query = this.where(query, operation.parameters); - } - - if (operation.op === 'forFeatures') { - query = this.forFeatures(query, operation.parameters); - } - - if (operation.op === 'beforeDate') { - query = this.beforeDate(query, operation.parameters); - } - - if (operation.op === 'betweenDate') { - query = this.betweenDate(query, operation.parameters); - } - }); - - const rows = await query; - return rows.map(this.rowToEvent); - } catch (e) { - return []; - } - } - - async queryCount(operations: IQueryOperations[]): Promise { - try { - let query: Knex.QueryBuilder = this.db.count().from(TABLE); - - operations.forEach((operation) => { - if (operation.op === 'where') { - query = this.where(query, operation.parameters); - } - - if (operation.op === 'forFeatures') { - query = this.forFeatures(query, operation.parameters); - } - - if (operation.op === 'beforeDate') { - query = this.beforeDate(query, operation.parameters); - } - - if (operation.op === 'betweenDate') { - query = this.betweenDate(query, operation.parameters); - } - }); - - const queryResult = await query.first(); - return parseInt(queryResult.count || 0); - } catch (e) { - return 0; - } - } - - where( - query: Knex.QueryBuilder, - parameters: { [key: string]: string }, - ): Knex.QueryBuilder { - return query.where(parameters); - } - - beforeDate( - query: Knex.QueryBuilder, - parameters: { dateAccessor: string; date: string }, - ): Knex.QueryBuilder { - return query.andWhere(parameters.dateAccessor, '>=', parameters.date); - } - - betweenDate( - query: Knex.QueryBuilder, - parameters: { dateAccessor: string; range: string[] }, - ): Knex.QueryBuilder { - if (parameters.range && parameters.range.length === 2) { - return query.andWhereBetween(parameters.dateAccessor, [ - parameters.range[0], - parameters.range[1], - ]); - } - - return query; - } - - select(): Knex.QueryBuilder { - return this.db.select(EVENT_COLUMNS).from(TABLE); - } - - forFeatures( - query: Knex.QueryBuilder, - parameters: IForFeaturesParams, - ): Knex.QueryBuilder { - return query - .where({ type: parameters.type, project: parameters.projectId }) - .whereIn('feature_name', parameters.features) - .whereIn('environment', parameters.environments); - } - - async get(key: number): Promise { - const row = await this.db(TABLE).where({ id: key }).first(); - return this.rowToEvent(row); - } - - async getAll(query?: Object): Promise { - return this.getEvents(query); - } - - async getEvents(query?: Object): Promise { - try { - let qB = this.db - .select(EVENT_COLUMNS) - .from(TABLE) - .limit(100) - .orderBy('created_at', 'desc'); - if (query) { - qB = qB.where(query); - } - const rows = await qB; - return rows.map(this.rowToEvent); - } catch (err) { - return []; - } - } - - async searchEvents(search: SearchEventsSchema = {}): Promise { - let query = this.db - .select(EVENT_COLUMNS) - .from(TABLE) - .limit(search.limit ?? 100) - .offset(search.offset ?? 0) - .orderBy('created_at', 'desc'); - - if (search.type) { - query = query.andWhere({ - type: search.type, - }); - } - - if (search.project) { - query = query.andWhere({ - project: search.project, - }); - } - - if (search.feature) { - query = query.andWhere({ - feature_name: search.feature, - }); - } - - if (search.query) { - query = query.where((where) => - where - .orWhereRaw('type::text ILIKE ?', `%${search.query}%`) - .orWhereRaw('created_by::text ILIKE ?', `%${search.query}%`) - .orWhereRaw('data::text ILIKE ?', `%${search.query}%`) - .orWhereRaw('tags::text ILIKE ?', `%${search.query}%`) - .orWhereRaw('pre_data::text ILIKE ?', `%${search.query}%`), - ); - } - - try { - return (await query).map(this.rowToEvent); - } catch (err) { - return []; - } - } - - rowToEvent(row: IEventTable): IEvent { - return { - id: row.id, - type: row.type as IEventType, - createdBy: row.created_by, - createdAt: row.created_at, - createdByUserId: row.created_by_user_id, - data: row.data, - preData: row.pre_data, - tags: row.tags || [], - featureName: row.feature_name, - project: row.project, - environment: row.environment, - }; - } - - eventToDbRow(e: IBaseEvent): Omit { - return { - type: e.type, - created_by: e.createdBy ?? 'admin', - created_by_user_id: e.createdByUserId, - data: Array.isArray(e.data) ? JSON.stringify(e.data) : e.data, - pre_data: Array.isArray(e.preData) - ? JSON.stringify(e.preData) - : e.preData, - // @ts-expect-error workaround for json-array - tags: JSON.stringify(e.tags), - feature_name: e.featureName, - project: e.project, - environment: e.environment, - }; - } - - setMaxListeners(number: number): EventEmitter { - return this.eventEmitter.setMaxListeners(number); - } - - on( - eventName: string | symbol, - listener: (...args: any[]) => void, - ): EventEmitter { - return this.eventEmitter.on(eventName, listener); - } - - emit(eventName: string | symbol, ...args: any[]): boolean { - return this.eventEmitter.emit(eventName, ...args); - } - - off( - eventName: string | symbol, - listener: (...args: any[]) => void, - ): EventEmitter { - return this.eventEmitter.off(eventName, listener); - } - - async setUnannouncedToAnnounced(): Promise { - const rows = await this.db(TABLE) - .update({ announced: true }) - .where('announced', false) - .returning(EVENT_COLUMNS); - return rows.map(this.rowToEvent); - } - - async publishUnannouncedEvents(): Promise { - const events = await this.setUnannouncedToAnnounced(); - - events.forEach((e) => this.eventEmitter.emit(e.type, e)); - } -} - +import EventStore from '../features/events/event-store'; +// For backward compatibility +export * from '../features/events/event-store'; export default EventStore; diff --git a/src/lib/db/index.ts b/src/lib/db/index.ts index ea06d9dc5b..715666f5ed 100644 --- a/src/lib/db/index.ts +++ b/src/lib/db/index.ts @@ -1,6 +1,6 @@ import { IUnleashConfig, IUnleashStores } from '../types'; -import EventStore from './event-store'; +import EventStore from '../features/events/event-store'; import FeatureToggleStore from '../features/feature-toggle/feature-toggle-store'; import FeatureTypeStore from './feature-type-store'; import StrategyStore from './strategy-store'; diff --git a/src/lib/features/events/createEventsService.ts b/src/lib/features/events/createEventsService.ts index 04412797b5..9f1a238d1d 100644 --- a/src/lib/features/events/createEventsService.ts +++ b/src/lib/features/events/createEventsService.ts @@ -1,7 +1,7 @@ import FakeEventStore from '../../../test/fixtures/fake-event-store'; import FakeFeatureTagStore from '../../../test/fixtures/fake-feature-tag-store'; import { Db } from '../../db/db'; -import EventStore from '../../db/event-store'; +import EventStore from './event-store'; import FeatureTagStore from '../../db/feature-tag-store'; import { EventService } from '../../services'; import { IUnleashConfig } from '../../types'; diff --git a/src/lib/services/event-service.test.ts b/src/lib/features/events/event-service.test.ts similarity index 72% rename from src/lib/services/event-service.test.ts rename to src/lib/features/events/event-service.test.ts index bd74a86251..d89115f123 100644 --- a/src/lib/services/event-service.test.ts +++ b/src/lib/features/events/event-service.test.ts @@ -1,7 +1,7 @@ -import { ADMIN_TOKEN_USER, IApiUser } from '../types'; -import { createTestConfig } from '../../test/config/test-config'; -import { createFakeEventsService } from '../../lib/features'; -import { ApiTokenType } from '../../lib/types/models/api-token'; +import { ADMIN_TOKEN_USER, IApiUser } from '../../types'; +import { createTestConfig } from '../../../test/config/test-config'; +import { createFakeEventsService } from '..'; +import { ApiTokenType } from '../../types/models/api-token'; test('when using an admin token should get the username of the token and the id from internalAdminTokenUserId', async () => { const adminToken: IApiUser = { diff --git a/src/lib/features/events/event-service.ts b/src/lib/features/events/event-service.ts new file mode 100644 index 0000000000..6d2c3b935b --- /dev/null +++ b/src/lib/features/events/event-service.ts @@ -0,0 +1,137 @@ +import { IUnleashConfig } from '../../types/option'; +import { IFeatureTagStore, IUnleashStores } from '../../types/stores'; +import { Logger } from '../../logger'; +import { IEventStore } from '../../types/stores/event-store'; +import { IBaseEvent, IEventList, IUserEvent } from '../../types/events'; +import { SearchEventsSchema } from '../../openapi/spec/search-events-schema'; +import EventEmitter from 'events'; +import { IApiUser, ITag, IUser } from '../../types'; +import { ApiTokenType } from '../../types/models/api-token'; +import { + extractUserIdFromUser, + extractUsernameFromUser, +} from '../../util/extract-user'; + +export default class EventService { + private logger: Logger; + + private eventStore: IEventStore; + + private featureTagStore: IFeatureTagStore; + + constructor( + { + eventStore, + featureTagStore, + }: Pick, + { getLogger }: Pick, + ) { + this.logger = getLogger('services/event-service.ts'); + this.eventStore = eventStore; + this.featureTagStore = featureTagStore; + } + + async getEvents(): Promise { + const totalEvents = await this.eventStore.count(); + const events = await this.eventStore.getEvents(); + return { + events, + totalEvents, + }; + } + + async searchEvents(search: SearchEventsSchema): Promise { + const totalEvents = await this.eventStore.filteredCount(search); + const events = await this.eventStore.searchEvents(search); + return { + events, + totalEvents, + }; + } + + async onEvent( + eventName: string | symbol, + listener: (...args: any[]) => void, + ): Promise { + return this.eventStore.on(eventName, listener); + } + + private async enhanceEventsWithTags( + events: IBaseEvent[], + ): Promise { + const featureNamesSet = new Set(); + for (const event of events) { + if (event.featureName && !event.tags) { + featureNamesSet.add(event.featureName); + } + } + + const featureTagsMap: Map = new Map(); + const allTagsInFeatures = await this.featureTagStore.getAllByFeatures( + Array.from(featureNamesSet), + ); + + for (const tag of allTagsInFeatures) { + const featureTags = featureTagsMap.get(tag.featureName) || []; + featureTags.push({ value: tag.tagValue, type: tag.tagType }); + featureTagsMap.set(tag.featureName, featureTags); + } + + for (const event of events) { + if (event.featureName && !event.tags) { + event.tags = featureTagsMap.get(event.featureName); + } + } + + return events; + } + + isAdminToken(user: IUser | IApiUser): boolean { + return (user as IApiUser)?.type === ApiTokenType.ADMIN; + } + + getUserDetails(user: IUser | IApiUser): { + createdBy: string; + createdByUserId: number; + } { + return { + createdBy: extractUsernameFromUser(user), + createdByUserId: extractUserIdFromUser(user), + }; + } + + /** + * @deprecated use storeUserEvent instead + */ + async storeEvent(event: IBaseEvent): Promise { + return this.storeEvents([event]); + } + + /** + * @deprecated use storeUserEvents instead + */ + async storeEvents(events: IBaseEvent[]): Promise { + let enhancedEvents = events; + for (const enhancer of [this.enhanceEventsWithTags.bind(this)]) { + enhancedEvents = await enhancer(enhancedEvents); + } + return this.eventStore.batchStore(enhancedEvents); + } + + async storeUserEvent(event: IUserEvent): Promise { + return this.storeUserEvents([event]); + } + + async storeUserEvents(events: IUserEvent[]): Promise { + let enhancedEvents = events.map(({ byUser, ...event }) => { + return { + ...event, + ...this.getUserDetails(byUser), + }; + }); + for (const enhancer of [this.enhanceEventsWithTags.bind(this)]) { + enhancedEvents = await enhancer(enhancedEvents); + } + return this.eventStore.batchStore(enhancedEvents); + } +} diff --git a/src/lib/db/event-store.test.ts b/src/lib/features/events/event-store.test.ts similarity index 93% rename from src/lib/db/event-store.test.ts rename to src/lib/features/events/event-store.test.ts index a382639442..07524beb05 100644 --- a/src/lib/db/event-store.test.ts +++ b/src/lib/features/events/event-store.test.ts @@ -1,8 +1,8 @@ import knex from 'knex'; import EventStore from './event-store'; -import getLogger from '../../test/fixtures/no-logger'; +import getLogger from '../../../test/fixtures/no-logger'; import { subHours, formatRFC3339 } from 'date-fns'; -import dbInit from '../../test/e2e/helpers/database-init'; +import dbInit from '../../../test/e2e/helpers/database-init'; beforeAll(() => { getLogger.setMuteError(true); diff --git a/src/lib/features/events/event-store.ts b/src/lib/features/events/event-store.ts new file mode 100644 index 0000000000..457b4007be --- /dev/null +++ b/src/lib/features/events/event-store.ts @@ -0,0 +1,433 @@ +import { + IEvent, + IBaseEvent, + SEGMENT_UPDATED, + FEATURE_IMPORT, + FEATURES_IMPORTED, + IEventType, +} from '../../types/events'; +import { LogProvider, Logger } from '../../logger'; +import { IEventStore } from '../../types/stores/event-store'; +import { ITag } from '../../types/model'; +import { SearchEventsSchema } from '../../openapi/spec/search-events-schema'; +import { sharedEventEmitter } from '../../util/anyEventEmitter'; +import { Db } from '../../db/db'; +import { Knex } from 'knex'; +import EventEmitter from 'events'; + +const EVENT_COLUMNS = [ + 'id', + 'type', + 'created_by', + 'created_at', + 'created_by_user_id', + 'data', + 'pre_data', + 'tags', + 'feature_name', + 'project', + 'environment', +] as const; + +export type IQueryOperations = + | IWhereOperation + | IBeforeDateOperation + | IBetweenDatesOperation + | IForFeaturesOperation; + +interface IWhereOperation { + op: 'where'; + parameters: { + [key: string]: string; + }; +} + +interface IBeforeDateOperation { + op: 'beforeDate'; + parameters: { + dateAccessor: string; + date: string; + }; +} + +interface IBetweenDatesOperation { + op: 'betweenDate'; + parameters: { + dateAccessor: string; + range: string[]; + }; +} + +interface IForFeaturesOperation { + op: 'forFeatures'; + parameters: IForFeaturesParams; +} + +interface IForFeaturesParams { + type: string; + projectId: string; + environments: string[]; + features: string[]; +} + +export interface IEventTable { + id: number; + type: string; + created_by: string; + created_at: Date; + created_by_user_id: number; + data?: any; + pre_data?: any; + feature_name?: string; + project?: string; + environment?: string; + tags: ITag[]; +} + +const TABLE = 'events'; + +class EventStore implements IEventStore { + private db: Db; + + // only one shared event emitter should exist across all event store instances + private eventEmitter: EventEmitter = sharedEventEmitter; + + private logger: Logger; + + // a new DB has to be injected per transaction + constructor(db: Db, getLogger: LogProvider) { + this.db = db; + this.logger = getLogger('event-store'); + } + + async store(event: IBaseEvent): Promise { + try { + await this.db(TABLE) + .insert(this.eventToDbRow(event)) + .returning(EVENT_COLUMNS); + } catch (error: unknown) { + this.logger.warn(`Failed to store "${event.type}" event: ${error}`); + } + } + + async count(): Promise { + const count = await this.db(TABLE) + .count>() + .first(); + if (!count) { + return 0; + } + if (typeof count.count === 'string') { + return parseInt(count.count, 10); + } else { + return count.count; + } + } + + async filteredCount(eventSearch: SearchEventsSchema): Promise { + let query = this.db(TABLE); + if (eventSearch.type) { + query = query.andWhere({ type: eventSearch.type }); + } + if (eventSearch.project) { + query = query.andWhere({ project: eventSearch.project }); + } + if (eventSearch.feature) { + query = query.andWhere({ feature_name: eventSearch.feature }); + } + const count = await query.count().first(); + if (!count) { + return 0; + } + if (typeof count.count === 'string') { + return parseInt(count.count, 10); + } else { + return count.count; + } + } + + async batchStore(events: IBaseEvent[]): Promise { + try { + await this.db(TABLE).insert(events.map(this.eventToDbRow)); + } catch (error: unknown) { + this.logger.warn(`Failed to store events: ${error}`); + } + } + + async getMaxRevisionId(largerThan: number = 0): Promise { + const row = await this.db(TABLE) + .max('id') + .where((builder) => + builder + .whereNotNull('feature_name') + .orWhereIn('type', [ + SEGMENT_UPDATED, + FEATURE_IMPORT, + FEATURES_IMPORTED, + ]), + ) + .andWhere('id', '>=', largerThan) + .first(); + return row?.max ?? 0; + } + + async delete(key: number): Promise { + await this.db(TABLE).where({ id: key }).del(); + } + + async deleteAll(): Promise { + await this.db(TABLE).del(); + } + + destroy(): void {} + + async exists(key: number): Promise { + const result = await this.db.raw( + `SELECT EXISTS (SELECT 1 FROM ${TABLE} WHERE id = ?) AS present`, + [key], + ); + const { present } = result.rows[0]; + return present; + } + + async query(operations: IQueryOperations[]): Promise { + try { + let query: Knex.QueryBuilder = this.select(); + + operations.forEach((operation) => { + if (operation.op === 'where') { + query = this.where(query, operation.parameters); + } + + if (operation.op === 'forFeatures') { + query = this.forFeatures(query, operation.parameters); + } + + if (operation.op === 'beforeDate') { + query = this.beforeDate(query, operation.parameters); + } + + if (operation.op === 'betweenDate') { + query = this.betweenDate(query, operation.parameters); + } + }); + + const rows = await query; + return rows.map(this.rowToEvent); + } catch (e) { + return []; + } + } + + async queryCount(operations: IQueryOperations[]): Promise { + try { + let query: Knex.QueryBuilder = this.db.count().from(TABLE); + + operations.forEach((operation) => { + if (operation.op === 'where') { + query = this.where(query, operation.parameters); + } + + if (operation.op === 'forFeatures') { + query = this.forFeatures(query, operation.parameters); + } + + if (operation.op === 'beforeDate') { + query = this.beforeDate(query, operation.parameters); + } + + if (operation.op === 'betweenDate') { + query = this.betweenDate(query, operation.parameters); + } + }); + + const queryResult = await query.first(); + return parseInt(queryResult.count || 0); + } catch (e) { + return 0; + } + } + + where( + query: Knex.QueryBuilder, + parameters: { [key: string]: string }, + ): Knex.QueryBuilder { + return query.where(parameters); + } + + beforeDate( + query: Knex.QueryBuilder, + parameters: { dateAccessor: string; date: string }, + ): Knex.QueryBuilder { + return query.andWhere(parameters.dateAccessor, '>=', parameters.date); + } + + betweenDate( + query: Knex.QueryBuilder, + parameters: { dateAccessor: string; range: string[] }, + ): Knex.QueryBuilder { + if (parameters.range && parameters.range.length === 2) { + return query.andWhereBetween(parameters.dateAccessor, [ + parameters.range[0], + parameters.range[1], + ]); + } + + return query; + } + + select(): Knex.QueryBuilder { + return this.db.select(EVENT_COLUMNS).from(TABLE); + } + + forFeatures( + query: Knex.QueryBuilder, + parameters: IForFeaturesParams, + ): Knex.QueryBuilder { + return query + .where({ type: parameters.type, project: parameters.projectId }) + .whereIn('feature_name', parameters.features) + .whereIn('environment', parameters.environments); + } + + async get(key: number): Promise { + const row = await this.db(TABLE).where({ id: key }).first(); + return this.rowToEvent(row); + } + + async getAll(query?: Object): Promise { + return this.getEvents(query); + } + + async getEvents(query?: Object): Promise { + try { + let qB = this.db + .select(EVENT_COLUMNS) + .from(TABLE) + .limit(100) + .orderBy('created_at', 'desc'); + if (query) { + qB = qB.where(query); + } + const rows = await qB; + return rows.map(this.rowToEvent); + } catch (err) { + return []; + } + } + + async searchEvents(search: SearchEventsSchema = {}): Promise { + let query = this.db + .select(EVENT_COLUMNS) + .from(TABLE) + .limit(search.limit ?? 100) + .offset(search.offset ?? 0) + .orderBy('created_at', 'desc'); + + if (search.type) { + query = query.andWhere({ + type: search.type, + }); + } + + if (search.project) { + query = query.andWhere({ + project: search.project, + }); + } + + if (search.feature) { + query = query.andWhere({ + feature_name: search.feature, + }); + } + + if (search.query) { + query = query.where((where) => + where + .orWhereRaw('type::text ILIKE ?', `%${search.query}%`) + .orWhereRaw('created_by::text ILIKE ?', `%${search.query}%`) + .orWhereRaw('data::text ILIKE ?', `%${search.query}%`) + .orWhereRaw('tags::text ILIKE ?', `%${search.query}%`) + .orWhereRaw('pre_data::text ILIKE ?', `%${search.query}%`), + ); + } + + try { + return (await query).map(this.rowToEvent); + } catch (err) { + return []; + } + } + + rowToEvent(row: IEventTable): IEvent { + return { + id: row.id, + type: row.type as IEventType, + createdBy: row.created_by, + createdAt: row.created_at, + createdByUserId: row.created_by_user_id, + data: row.data, + preData: row.pre_data, + tags: row.tags || [], + featureName: row.feature_name, + project: row.project, + environment: row.environment, + }; + } + + eventToDbRow(e: IBaseEvent): Omit { + return { + type: e.type, + created_by: e.createdBy ?? 'admin', + created_by_user_id: e.createdByUserId, + data: Array.isArray(e.data) ? JSON.stringify(e.data) : e.data, + pre_data: Array.isArray(e.preData) + ? JSON.stringify(e.preData) + : e.preData, + // @ts-expect-error workaround for json-array + tags: JSON.stringify(e.tags), + feature_name: e.featureName, + project: e.project, + environment: e.environment, + }; + } + + setMaxListeners(number: number): EventEmitter { + return this.eventEmitter.setMaxListeners(number); + } + + on( + eventName: string | symbol, + listener: (...args: any[]) => void, + ): EventEmitter { + return this.eventEmitter.on(eventName, listener); + } + + emit(eventName: string | symbol, ...args: any[]): boolean { + return this.eventEmitter.emit(eventName, ...args); + } + + off( + eventName: string | symbol, + listener: (...args: any[]) => void, + ): EventEmitter { + return this.eventEmitter.off(eventName, listener); + } + + async setUnannouncedToAnnounced(): Promise { + const rows = await this.db(TABLE) + .update({ announced: true }) + .where('announced', false) + .returning(EVENT_COLUMNS); + return rows.map(this.rowToEvent); + } + + async publishUnannouncedEvents(): Promise { + const events = await this.setUnannouncedToAnnounced(); + + events.forEach((e) => this.eventEmitter.emit(e.type, e)); + } +} + +export default EventStore; diff --git a/src/lib/features/export-import-toggles/createExportImportService.ts b/src/lib/features/export-import-toggles/createExportImportService.ts index 7623b34fdd..29636f392a 100644 --- a/src/lib/features/export-import-toggles/createExportImportService.ts +++ b/src/lib/features/export-import-toggles/createExportImportService.ts @@ -38,7 +38,7 @@ import FakeEventStore from '../../../test/fixtures/fake-event-store'; import FakeFeatureStrategiesStore from '../feature-toggle/fakes/fake-feature-strategies-store'; import FakeFeatureEnvironmentStore from '../../../test/fixtures/fake-feature-environment-store'; import FakeStrategiesStore from '../../../test/fixtures/fake-strategies-store'; -import EventStore from '../../db/event-store'; +import EventStore from '../events/event-store'; import { createFakePrivateProjectChecker, createPrivateProjectChecker, diff --git a/src/lib/features/feature-toggle/feature-toggle-service.ts b/src/lib/features/feature-toggle/feature-toggle-service.ts index 23a165a32b..226a2dd6b3 100644 --- a/src/lib/features/feature-toggle/feature-toggle-service.ts +++ b/src/lib/features/feature-toggle/feature-toggle-service.ts @@ -101,7 +101,7 @@ import { IChangeRequestAccessReadModel } from '../change-request-access-service/ import { checkFeatureFlagNamesAgainstPattern } from '../feature-naming-pattern/feature-naming-validation'; import { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType'; import { IDependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model-type'; -import EventService from '../../services/event-service'; +import EventService from '../events/event-service'; import { DependentFeaturesService } from '../dependent-features/dependent-features-service'; import { FeatureToggleInsert } from './feature-toggle-store'; diff --git a/src/lib/features/maintenance/maintenance-service.test.ts b/src/lib/features/maintenance/maintenance-service.test.ts index bbbaadb82e..7e7d7ae63b 100644 --- a/src/lib/features/maintenance/maintenance-service.test.ts +++ b/src/lib/features/maintenance/maintenance-service.test.ts @@ -3,7 +3,7 @@ import MaintenanceService from './maintenance-service'; import SettingService from '../../services/setting-service'; import { createTestConfig } from '../../../test/config/test-config'; import FakeSettingStore from '../../../test/fixtures/fake-setting-store'; -import EventService from '../../services/event-service'; +import EventService from '../events/event-service'; test('Scheduler should run scheduled functions if maintenance mode is off', async () => { const config = createTestConfig(); diff --git a/src/lib/features/project-environments/environment-service.ts b/src/lib/features/project-environments/environment-service.ts index bf2e77c129..028568dfbf 100644 --- a/src/lib/features/project-environments/environment-service.ts +++ b/src/lib/features/project-environments/environment-service.ts @@ -21,7 +21,7 @@ import { IProjectStore } from '../../types/stores/project-store'; import MinimumOneEnvironmentError from '../../error/minimum-one-environment-error'; import { IFlagResolver } from '../../types/experimental'; import { CreateFeatureStrategySchema } from '../../openapi'; -import EventService from '../../services/event-service'; +import EventService from '../events/event-service'; export default class EnvironmentService { private logger: Logger; diff --git a/src/lib/features/project/createProjectService.ts b/src/lib/features/project/createProjectService.ts index a34145314c..1361c80a69 100644 --- a/src/lib/features/project/createProjectService.ts +++ b/src/lib/features/project/createProjectService.ts @@ -1,5 +1,5 @@ import { Db, IUnleashConfig } from '../../server-impl'; -import EventStore from '../../db/event-store'; +import EventStore from '../events/event-store'; import GroupStore from '../../db/group-store'; import { AccountStore } from '../../db/account-store'; import EnvironmentStore from '../project-environments/environment-store'; diff --git a/src/lib/features/scheduler/scheduler-service.test.ts b/src/lib/features/scheduler/scheduler-service.test.ts index 5e1b1b7836..adc8b58225 100644 --- a/src/lib/features/scheduler/scheduler-service.test.ts +++ b/src/lib/features/scheduler/scheduler-service.test.ts @@ -4,7 +4,7 @@ import MaintenanceService from '../maintenance/maintenance-service'; import { createTestConfig } from '../../../test/config/test-config'; import SettingService from '../../services/setting-service'; import FakeSettingStore from '../../../test/fixtures/fake-setting-store'; -import EventService from '../../services/event-service'; +import EventService from '../events/event-service'; import { SCHEDULER_JOB_TIME } from '../../metric-events'; import EventEmitter from 'events'; diff --git a/src/lib/features/tag-type/tag-type-service.ts b/src/lib/features/tag-type/tag-type-service.ts index b330620af8..7deb36d0f1 100644 --- a/src/lib/features/tag-type/tag-type-service.ts +++ b/src/lib/features/tag-type/tag-type-service.ts @@ -12,7 +12,7 @@ import { import { Logger } from '../../logger'; import { ITagType, ITagTypeStore } from './tag-type-store-type'; import { IUnleashConfig } from '../../types/option'; -import EventService from '../../services/event-service'; +import EventService from '../events/event-service'; import { SYSTEM_USER } from '../../types'; export default class TagTypeService { diff --git a/src/lib/routes/admin-api/event.ts b/src/lib/routes/admin-api/event.ts index 107f54def6..60f8052e85 100644 --- a/src/lib/routes/admin-api/event.ts +++ b/src/lib/routes/admin-api/event.ts @@ -1,7 +1,7 @@ import { Request, Response } from 'express'; import { IUnleashConfig } from '../../types/option'; import { IUnleashServices } from '../../types/services'; -import EventService from '../../services/event-service'; +import EventService from '../../features/events/event-service'; import { ADMIN, NONE } from '../../types/permissions'; import { IEvent, IEventList } from '../../types/events'; import Controller from '../controller'; diff --git a/src/lib/services/access-service.ts b/src/lib/services/access-service.ts index 5252857b9b..2c5d689c62 100644 --- a/src/lib/services/access-service.ts +++ b/src/lib/services/access-service.ts @@ -48,7 +48,7 @@ import { ROLE_UPDATED, SYSTEM_USER, } from '../types'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; const { ADMIN } = permissions; diff --git a/src/lib/services/addon-service.test.ts b/src/lib/services/addon-service.test.ts index 535081985e..e13b2a6966 100644 --- a/src/lib/services/addon-service.test.ts +++ b/src/lib/services/addon-service.test.ts @@ -14,7 +14,7 @@ import AddonService from './addon-service'; import { IAddonDto } from '../types/stores/addon-store'; import SimpleAddon from './addon-service-test-simple-addon'; import { IAddonProviders } from '../addons'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; import { SYSTEM_USER } from '../types'; const MASKED_VALUE = '*****'; diff --git a/src/lib/services/addon-service.ts b/src/lib/services/addon-service.ts index c063e43f8f..648cf341fa 100644 --- a/src/lib/services/addon-service.ts +++ b/src/lib/services/addon-service.ts @@ -11,7 +11,7 @@ import { IAddon, IAddonDto, IAddonStore } from '../types/stores/addon-store'; import { IUnleashStores, IUnleashConfig, SYSTEM_USER } from '../types'; import { IAddonDefinition } from '../types/model'; import { minutesToMilliseconds } from 'date-fns'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; import { omitKeys } from '../util'; const SUPPORTED_EVENTS = Object.keys(events).map((k) => events[k]); diff --git a/src/lib/services/api-token-service.test.ts b/src/lib/services/api-token-service.test.ts index 25aad84b7d..da63439d46 100644 --- a/src/lib/services/api-token-service.test.ts +++ b/src/lib/services/api-token-service.test.ts @@ -12,7 +12,7 @@ import { API_TOKEN_UPDATED, } from '../types'; import { addDays } from 'date-fns'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store'; import { createFakeEventsService } from '../../lib/features'; diff --git a/src/lib/services/api-token-service.ts b/src/lib/services/api-token-service.ts index 49eef45f54..3acf9c20a7 100644 --- a/src/lib/services/api-token-service.ts +++ b/src/lib/services/api-token-service.ts @@ -33,7 +33,7 @@ import { extractUsernameFromUser, omitKeys, } from '../util'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; const resolveTokenPermissions = (tokenType: string) => { if (tokenType === ApiTokenType.ADMIN) { diff --git a/src/lib/services/context-service.ts b/src/lib/services/context-service.ts index 554cd94869..73bc807e93 100644 --- a/src/lib/services/context-service.ts +++ b/src/lib/services/context-service.ts @@ -10,7 +10,7 @@ import { IUnleashConfig } from '../types/option'; import { ContextFieldStrategiesSchema } from '../openapi/spec/context-field-strategies-schema'; import { IFeatureStrategy, IFlagResolver } from '../types'; import { IPrivateProjectChecker } from '../features/private-project/privateProjectCheckerType'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; const { contextSchema, nameSchema } = require('./context-schema'); const NameExistsError = require('../error/name-exists-error'); diff --git a/src/lib/services/event-service.ts b/src/lib/services/event-service.ts index 01223b23e0..52608715df 100644 --- a/src/lib/services/event-service.ts +++ b/src/lib/services/event-service.ts @@ -1,141 +1,4 @@ -import { IUnleashConfig } from '../types/option'; -import { IFeatureTagStore, IUnleashStores } from '../types/stores'; -import { Logger } from '../logger'; -import { IEventStore } from '../types/stores/event-store'; -import { IBaseEvent, IEventList, IUserEvent } from '../types/events'; -import { SearchEventsSchema } from '../openapi/spec/search-events-schema'; -import EventEmitter from 'events'; -import { ADMIN_TOKEN_USER, IApiUser, ITag, IUser, SYSTEM_USER } from '../types'; -import { ApiTokenType } from '../../lib/types/models/api-token'; - -export default class EventService { - private logger: Logger; - - private eventStore: IEventStore; - - private featureTagStore: IFeatureTagStore; - - constructor( - { - eventStore, - featureTagStore, - }: Pick, - { getLogger }: Pick, - ) { - this.logger = getLogger('services/event-service.ts'); - this.eventStore = eventStore; - this.featureTagStore = featureTagStore; - } - - async getEvents(): Promise { - const totalEvents = await this.eventStore.count(); - const events = await this.eventStore.getEvents(); - return { - events, - totalEvents, - }; - } - - async searchEvents(search: SearchEventsSchema): Promise { - const totalEvents = await this.eventStore.filteredCount(search); - const events = await this.eventStore.searchEvents(search); - return { - events, - totalEvents, - }; - } - - async onEvent( - eventName: string | symbol, - listener: (...args: any[]) => void, - ): Promise { - return this.eventStore.on(eventName, listener); - } - - private async enhanceEventsWithTags( - events: IBaseEvent[], - ): Promise { - const featureNamesSet = new Set(); - for (const event of events) { - if (event.featureName && !event.tags) { - featureNamesSet.add(event.featureName); - } - } - - const featureTagsMap: Map = new Map(); - const allTagsInFeatures = await this.featureTagStore.getAllByFeatures( - Array.from(featureNamesSet), - ); - - for (const tag of allTagsInFeatures) { - const featureTags = featureTagsMap.get(tag.featureName) || []; - featureTags.push({ value: tag.tagValue, type: tag.tagType }); - featureTagsMap.set(tag.featureName, featureTags); - } - - for (const event of events) { - if (event.featureName && !event.tags) { - event.tags = featureTagsMap.get(event.featureName); - } - } - - return events; - } - - isAdminToken(user: IUser | IApiUser): boolean { - return (user as IApiUser)?.type === ApiTokenType.ADMIN; - } - - getUserDetails(user: IUser | IApiUser): { - createdBy: string; - createdByUserId: number; - } { - return { - createdBy: - (user as IUser)?.email || - user?.username || - (this.isAdminToken(user) - ? ADMIN_TOKEN_USER.username - : SYSTEM_USER.username), - createdByUserId: - (user as IUser)?.id || - (user as IApiUser)?.internalAdminTokenUserId || - SYSTEM_USER.id, - }; - } - - /** - * @deprecated use storeUserEvent instead - */ - async storeEvent(event: IBaseEvent): Promise { - return this.storeEvents([event]); - } - - /** - * @deprecated use storeUserEvents instead - */ - async storeEvents(events: IBaseEvent[]): Promise { - let enhancedEvents = events; - for (const enhancer of [this.enhanceEventsWithTags.bind(this)]) { - enhancedEvents = await enhancer(enhancedEvents); - } - return this.eventStore.batchStore(enhancedEvents); - } - - async storeUserEvent(event: IUserEvent): Promise { - return this.storeUserEvents([event]); - } - - async storeUserEvents(events: IUserEvent[]): Promise { - let enhancedEvents = events.map(({ byUser, ...event }) => { - return { - ...event, - ...this.getUserDetails(byUser), - }; - }); - for (const enhancer of [this.enhanceEventsWithTags.bind(this)]) { - enhancedEvents = await enhancer(enhancedEvents); - } - return this.eventStore.batchStore(enhancedEvents); - } -} +import EventService from '../features/events/event-service'; +// For backward compatibility +export * from '../features/events/event-service'; +export default EventService; diff --git a/src/lib/services/favorites-service.ts b/src/lib/services/favorites-service.ts index 515038bd35..e859e72fed 100644 --- a/src/lib/services/favorites-service.ts +++ b/src/lib/services/favorites-service.ts @@ -12,7 +12,7 @@ import { import { IUser } from '../types/user'; import { extractUsernameFromUser } from '../util'; import { IFavoriteProjectKey } from '../types/stores/favorite-projects'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; export interface IFavoriteFeatureProps { feature: string; diff --git a/src/lib/services/feature-service-potentially-stale.test.ts b/src/lib/services/feature-service-potentially-stale.test.ts index 1db2214a83..eb4289e1f6 100644 --- a/src/lib/services/feature-service-potentially-stale.test.ts +++ b/src/lib/services/feature-service-potentially-stale.test.ts @@ -11,7 +11,7 @@ import { IChangeRequestAccessReadModel } from '../features/change-request-access import { ISegmentService } from '../segments/segment-service-interface'; import { IPrivateProjectChecker } from '../features/private-project/privateProjectCheckerType'; import { IDependentFeaturesReadModel } from '../features/dependent-features/dependent-features-read-model-type'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store'; import { DependentFeaturesService } from '../features/dependent-features/dependent-features-service'; diff --git a/src/lib/services/feature-tag-service.ts b/src/lib/services/feature-tag-service.ts index 8206d0dc94..672b51d7b6 100644 --- a/src/lib/services/feature-tag-service.ts +++ b/src/lib/services/feature-tag-service.ts @@ -12,7 +12,7 @@ import { import { ITagStore } from '../types/stores/tag-store'; import { ITag } from '../types/model'; import { BadDataError, FOREIGN_KEY_VIOLATION } from '../../lib/error'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; class FeatureTagService { private tagStore: ITagStore; diff --git a/src/lib/services/feature-type-service.ts b/src/lib/services/feature-type-service.ts index 20309de0f7..b3546f1f95 100644 --- a/src/lib/services/feature-type-service.ts +++ b/src/lib/services/feature-type-service.ts @@ -6,7 +6,7 @@ import { IFeatureTypeStore, } from '../types/stores/feature-type-store'; import NotFoundError from '../error/notfound-error'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; import { FEATURE_TYPE_UPDATED, IUser } from '../types'; import { extractUsernameFromUser } from '../util'; diff --git a/src/lib/services/group-service.ts b/src/lib/services/group-service.ts index 26920c7bd9..df8df4d97e 100644 --- a/src/lib/services/group-service.ts +++ b/src/lib/services/group-service.ts @@ -22,7 +22,7 @@ import { import NameExistsError from '../error/name-exists-error'; import { IAccountStore } from '../types/stores/account-store'; import { IUser } from '../types/user'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; export class GroupService { private groupStore: IGroupStore; diff --git a/src/lib/services/index.ts b/src/lib/services/index.ts index acb5b45c2a..b05503453e 100644 --- a/src/lib/services/index.ts +++ b/src/lib/services/index.ts @@ -1,6 +1,6 @@ import { IUnleashConfig, IUnleashStores, IUnleashServices } from '../types'; import FeatureTypeService from './feature-type-service'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; import HealthService from './health-service'; import ProjectService from './project-service'; diff --git a/src/lib/services/pat-service.ts b/src/lib/services/pat-service.ts index 3bdcb108d0..825ac34f84 100644 --- a/src/lib/services/pat-service.ts +++ b/src/lib/services/pat-service.ts @@ -9,7 +9,7 @@ import BadDataError from '../error/bad-data-error'; import NameExistsError from '../error/name-exists-error'; import { OperationDeniedError } from '../error/operation-denied-error'; import { PAT_LIMIT } from '../util/constants'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; export default class PatService { private config: IUnleashConfig; diff --git a/src/lib/services/project-service.ts b/src/lib/services/project-service.ts index 43c856be5f..c8eb50f5e9 100644 --- a/src/lib/services/project-service.ts +++ b/src/lib/services/project-service.ts @@ -66,7 +66,7 @@ import { BadDataError, PermissionError } from '../error'; import { ProjectDoraMetricsSchema } from '../openapi'; import { checkFeatureNamingData } from '../features/feature-naming-pattern/feature-naming-validation'; import { IPrivateProjectChecker } from '../features/private-project/privateProjectCheckerType'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; const getCreatedBy = (user: IUser) => user.email || user.username || 'unknown'; diff --git a/src/lib/services/public-signup-token-service.ts b/src/lib/services/public-signup-token-service.ts index 947578fedd..31403c5c5d 100644 --- a/src/lib/services/public-signup-token-service.ts +++ b/src/lib/services/public-signup-token-service.ts @@ -17,7 +17,7 @@ import UserService from './user-service'; import { IUser } from '../types/user'; import { URL } from 'url'; import { add } from 'date-fns'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; export class PublicSignupTokenService { private store: IPublicSignupTokenStore; diff --git a/src/lib/services/scheduler-service.test.ts b/src/lib/services/scheduler-service.test.ts index 64fbd55e59..88b0d42871 100644 --- a/src/lib/services/scheduler-service.test.ts +++ b/src/lib/services/scheduler-service.test.ts @@ -3,7 +3,7 @@ import { SchedulerService } from '../features/scheduler/scheduler-service'; import { createTestConfig } from '../../test/config/test-config'; import FakeSettingStore from '../../test/fixtures/fake-setting-store'; import SettingService from './setting-service'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; import MaintenanceService from '../features/maintenance/maintenance-service'; function ms(timeMs: number) { diff --git a/src/lib/services/segment-service.ts b/src/lib/services/segment-service.ts index ccd8b82233..ad600652d9 100644 --- a/src/lib/services/segment-service.ts +++ b/src/lib/services/segment-service.ts @@ -26,7 +26,7 @@ import { import { PermissionError } from '../error'; import { IChangeRequestAccessReadModel } from '../features/change-request-access-service/change-request-access-read-model'; import { IPrivateProjectChecker } from '../features/private-project/privateProjectCheckerType'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; import { IChangeRequestSegmentUsageReadModel } from '../features/change-request-segment-usage-service/change-request-segment-usage-read-model'; export class SegmentService implements ISegmentService { diff --git a/src/lib/services/setting-service.ts b/src/lib/services/setting-service.ts index 6cc45c4c61..72da01e234 100644 --- a/src/lib/services/setting-service.ts +++ b/src/lib/services/setting-service.ts @@ -7,7 +7,7 @@ import { SettingDeletedEvent, SettingUpdatedEvent, } from '../types/events'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; export default class SettingService { private config: IUnleashConfig; diff --git a/src/lib/services/state-service.test.ts b/src/lib/services/state-service.test.ts index 4799cc00b6..839b6ae8bf 100644 --- a/src/lib/services/state-service.test.ts +++ b/src/lib/services/state-service.test.ts @@ -13,7 +13,7 @@ import { } from '../types/events'; import { GLOBAL_ENV } from '../types/environment'; import variantsExportV3 from '../../test/examples/variantsexport_v3.json'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; import { SYSTEM_USER_ID } from '../types'; const oldExportExample = require('./state-service-export-v1.json'); const TESTUSERID = 3333; diff --git a/src/lib/services/state-service.ts b/src/lib/services/state-service.ts index a03d78559c..9a099ec405 100644 --- a/src/lib/services/state-service.ts +++ b/src/lib/services/state-service.ts @@ -52,7 +52,7 @@ import { DEFAULT_ENV } from '../util/constants'; import { GLOBAL_ENV } from '../types/environment'; import { ISegmentStore } from '../types/stores/segment-store'; import { PartialSome } from '../types/partial'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; export interface IBackupOption { includeFeatureToggles: boolean; diff --git a/src/lib/services/strategy-service.ts b/src/lib/services/strategy-service.ts index 20d9b67871..4223af5530 100644 --- a/src/lib/services/strategy-service.ts +++ b/src/lib/services/strategy-service.ts @@ -7,7 +7,7 @@ import { IStrategyStore, } from '../types/stores/strategy-store'; import NotFoundError from '../error/notfound-error'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; const strategySchema = require('./strategy-schema'); const NameExistsError = require('../error/name-exists-error'); diff --git a/src/lib/services/tag-service.ts b/src/lib/services/tag-service.ts index e7165efcb6..a55d14827c 100644 --- a/src/lib/services/tag-service.ts +++ b/src/lib/services/tag-service.ts @@ -6,7 +6,7 @@ import { IUnleashStores } from '../types/stores'; import { IUnleashConfig } from '../types/option'; import { ITagStore } from '../types/stores/tag-store'; import { ITag } from '../types/model'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; export default class TagService { private tagStore: ITagStore; diff --git a/src/lib/services/user-service.test.ts b/src/lib/services/user-service.test.ts index 9eeb514aa3..e1451fdb9e 100644 --- a/src/lib/services/user-service.test.ts +++ b/src/lib/services/user-service.test.ts @@ -14,7 +14,7 @@ import User from '../types/user'; import FakeResetTokenStore from '../../test/fixtures/fake-reset-token-store'; import SettingService from './setting-service'; import FakeSettingStore from '../../test/fixtures/fake-setting-store'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store'; const config: IUnleashConfig = createTestConfig(); diff --git a/src/lib/services/user-service.ts b/src/lib/services/user-service.ts index 8044df29ff..60d83cca8b 100644 --- a/src/lib/services/user-service.ts +++ b/src/lib/services/user-service.ts @@ -31,7 +31,7 @@ import BadDataError from '../error/bad-data-error'; import { isDefined } from '../util/isDefined'; import { TokenUserSchema } from '../openapi/spec/token-user-schema'; import PasswordMismatch from '../error/password-mismatch'; -import EventService from './event-service'; +import EventService from '../features/events/event-service'; const systemUser = new User({ id: -1, username: 'system' }); diff --git a/src/lib/types/services.ts b/src/lib/types/services.ts index 8305cb5a12..e4fbbb26ea 100644 --- a/src/lib/types/services.ts +++ b/src/lib/types/services.ts @@ -13,7 +13,7 @@ import { EmailService } from '../services/email-service'; import UserService from '../services/user-service'; import ResetTokenService from '../services/reset-token-service'; import FeatureTypeService from '../services/feature-type-service'; -import EventService from '../services/event-service'; +import EventService from '../features/events/event-service'; import HealthService from '../services/health-service'; import SettingService from '../services/setting-service'; import SessionService from '../services/session-service'; diff --git a/src/lib/types/stores/event-store.ts b/src/lib/types/stores/event-store.ts index d18aa6c2bc..235d5600bc 100644 --- a/src/lib/types/stores/event-store.ts +++ b/src/lib/types/stores/event-store.ts @@ -2,7 +2,7 @@ import { IBaseEvent, IEvent } from '../events'; import { Store } from './store'; import { SearchEventsSchema } from '../../openapi/spec/search-events-schema'; import EventEmitter from 'events'; -import { IQueryOperations } from '../../db/event-store'; +import { IQueryOperations } from '../../features/events/event-store'; export interface IEventStore extends Store, diff --git a/src/test/fixtures/fake-event-store.ts b/src/test/fixtures/fake-event-store.ts index 6e8c4720c2..e625c2c211 100644 --- a/src/test/fixtures/fake-event-store.ts +++ b/src/test/fixtures/fake-event-store.ts @@ -1,7 +1,7 @@ import { IEventStore } from '../../lib/types/stores/event-store'; import { IEvent } from '../../lib/types/events'; import { sharedEventEmitter } from '../../lib/util/anyEventEmitter'; -import { IQueryOperations } from '../../lib/db/event-store'; +import { IQueryOperations } from '../../lib/features/events/event-store'; import { SearchEventsSchema } from '../../lib/openapi'; import EventEmitter from 'events';