diff --git a/src/lib/db/event-store.ts b/src/lib/db/event-store.ts index 918c6f87a7..1911ea8687 100644 --- a/src/lib/db/event-store.ts +++ b/src/lib/db/event-store.ts @@ -90,18 +90,21 @@ class EventStore extends EventEmitter implements IEventStore { return this.rowToEvent(row); } - async getAll(): Promise { - return this.getEvents(); + async getAll(query?: Object): Promise { + return this.getEvents(query); } - async getEvents(): Promise { + async getEvents(query?: Object): Promise { try { - const rows = await this.db + 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 []; diff --git a/src/lib/services/feature-toggle-service-v2.ts b/src/lib/services/feature-toggle-service-v2.ts index 9aa66d8618..a66bcdccb4 100644 --- a/src/lib/services/feature-toggle-service-v2.ts +++ b/src/lib/services/feature-toggle-service-v2.ts @@ -10,6 +10,8 @@ import { FEATURE_ARCHIVED, FEATURE_CREATED, FEATURE_DELETED, + FEATURE_ENVIRONMENT_DISABLED, + FEATURE_ENVIRONMENT_ENABLED, FEATURE_METADATA_UPDATED, FEATURE_REVIVED, FEATURE_STALE_OFF, @@ -569,6 +571,16 @@ class FeatureToggleServiceV2 { project: projectId, environment, }); + await this.eventStore.store({ + type: enabled + ? FEATURE_ENVIRONMENT_ENABLED + : FEATURE_ENVIRONMENT_DISABLED, + createdBy: userName, + data, + tags, + project: projectId, + environment, + }); return feature; } throw new NotFoundError( diff --git a/src/lib/types/events.ts b/src/lib/types/events.ts index b50a96efe6..570317ea14 100644 --- a/src/lib/types/events.ts +++ b/src/lib/types/events.ts @@ -50,3 +50,5 @@ export const USER_UPDATED = 'user-updated'; export const USER_DELETED = 'user-deleted'; export const DROP_ENVIRONMENTS = 'drop-environments'; export const ENVIRONMENT_IMPORT = 'environment-import'; +export const FEATURE_ENVIRONMENT_ENABLED = 'feature-environment-enabled'; +export const FEATURE_ENVIRONMENT_DISABLED = 'feature-environment-disabled'; diff --git a/src/test/e2e/api/admin/project/feature.strategy.e2e.test.ts b/src/test/e2e/api/admin/project/feature.strategy.e2e.test.ts index 4b8552054d..aed55e9253 100644 --- a/src/test/e2e/api/admin/project/feature.strategy.e2e.test.ts +++ b/src/test/e2e/api/admin/project/feature.strategy.e2e.test.ts @@ -2,6 +2,10 @@ import dbInit, { ITestDb } from '../../../helpers/database-init'; import { IUnleashTest, setupApp } from '../../../helpers/test-helper'; import getLogger from '../../../../fixtures/no-logger'; import { DEFAULT_ENV } from '../../../../../lib/util/constants'; +import { + FEATURE_ENVIRONMENT_DISABLED, + FEATURE_ENVIRONMENT_ENABLED, +} from '../../../../../lib/types/events'; let app: IUnleashTest; let db: ITestDb; @@ -911,6 +915,93 @@ test('Can not enable environment for feature without strategies', async () => { expect(enabledFeatureEnv.type).toBe('test'); }); }); +test('Enabling environment creates a FEATURE_ENVIRONMENT_ENABLED event', async () => { + const environment = 'environment_enabled_env'; + const featureName = 'com.test.enable.environment.event.sent'; + + // Create environment + await db.stores.environmentStore.create({ + name: environment, + displayName: 'Enable feature for environment', + type: 'test', + }); + // Connect environment to project + await app.request + .post('/api/admin/projects/default/environments') + .send({ environment }) + .expect(200); + + // Create feature + await app.request + .post('/api/admin/projects/default/features') + .send({ + name: featureName, + }) + .set('Content-Type', 'application/json') + .expect(201); + await app.request + .post( + `/api/admin/projects/default/features/${featureName}/environments/${environment}/strategies`, + ) + .send({ name: 'default', constraints: [], properties: {} }) + .expect(200); + + await app.request + .post( + `/api/admin/projects/default/features/${featureName}/environments/${environment}/on`, + ) + .set('Content-Type', 'application/json') + .expect(200); + const events = await db.stores.eventStore.getAll({ + type: FEATURE_ENVIRONMENT_ENABLED, + }); + const enabledEvents = events.filter((e) => e.data.name === featureName); + expect(enabledEvents).toHaveLength(1); +}); +test('Disabling environment creates a FEATURE_ENVIRONMENT_DISABLED event', async () => { + const environment = 'environment_disabled_env'; + const featureName = 'com.test.enable.environment_disabled.sent'; + + // Create environment + await db.stores.environmentStore.create({ + name: environment, + displayName: 'Enable feature for environment', + type: 'test', + }); + // Connect environment to project + await app.request + .post('/api/admin/projects/default/environments') + .send({ environment }) + .expect(200); + + // Create feature + await app.request + .post('/api/admin/projects/default/features') + .send({ + name: featureName, + }) + .set('Content-Type', 'application/json') + .expect(201); + await app.request + .post( + `/api/admin/projects/default/features/${featureName}/environments/${environment}/strategies`, + ) + .send({ name: 'default', constraints: [], properties: {} }) + .expect(200); + + await app.request + .post( + `/api/admin/projects/default/features/${featureName}/environments/${environment}/off`, + ) + .set('Content-Type', 'application/json') + .expect(200); + + const events = await db.stores.eventStore.getAll({ + type: FEATURE_ENVIRONMENT_DISABLED, + }); + const ourFeatureEvent = events.find((e) => e.data.name === featureName); + expect(ourFeatureEvent).toBeTruthy(); +}); test('Can delete strategy from feature toggle', async () => { const envName = 'del-strategy'; diff --git a/src/test/e2e/services/feature-toggle-service-v2.e2e.test.ts b/src/test/e2e/services/feature-toggle-service-v2.e2e.test.ts index 45a7e2bf08..e61aee4c9c 100644 --- a/src/test/e2e/services/feature-toggle-service-v2.e2e.test.ts +++ b/src/test/e2e/services/feature-toggle-service-v2.e2e.test.ts @@ -118,9 +118,10 @@ test('Should include legacy props in event log when updating strategy configurat ); const events = await eventService.getEventsForToggle(featureName); - expect(events[0].type).toBe(FEATURE_UPDATED); - expect(events[0].data.enabled).toBe(true); - expect(events[0].data.strategies).toBeDefined(); + const updatedEvent = events.find((e) => e.type === FEATURE_UPDATED); + expect(updatedEvent.type).toBe(FEATURE_UPDATED); + expect(updatedEvent.data.enabled).toBe(true); + expect(updatedEvent.data.strategies).toBeDefined(); }); test('Should be able to get strategy by id', async () => {