mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	task: Add events for setting-service (#1814)
* task: Add events for setting-service
This commit is contained in:
		
							parent
							
								
									012da8469f
								
							
						
					
					
						commit
						09fa031e0f
					
				| @ -121,7 +121,7 @@ class EventStore extends EventEmitter implements IEventStore { | |||||||
|                 .select(EVENT_COLUMNS) |                 .select(EVENT_COLUMNS) | ||||||
|                 .from(TABLE) |                 .from(TABLE) | ||||||
|                 .limit(100) |                 .limit(100) | ||||||
|                 .whereRaw("data ->> 'name' = ?", [name]) |                 .where('type', name) | ||||||
|                 .andWhere( |                 .andWhere( | ||||||
|                     'id', |                     'id', | ||||||
|                     '>=', |                     '>=', | ||||||
|  | |||||||
| @ -2,30 +2,67 @@ import { IUnleashConfig } from '../types/option'; | |||||||
| import { IUnleashStores } from '../types/stores'; | import { IUnleashStores } from '../types/stores'; | ||||||
| import { Logger } from '../logger'; | import { Logger } from '../logger'; | ||||||
| import { ISettingStore } from '../types/stores/settings-store'; | import { ISettingStore } from '../types/stores/settings-store'; | ||||||
|  | import { IEventStore } from '../types/stores/event-store'; | ||||||
|  | import { | ||||||
|  |     SettingCreatedEvent, | ||||||
|  |     SettingDeletedEvent, | ||||||
|  |     SettingUpdatedEvent, | ||||||
|  | } from '../types/events'; | ||||||
| 
 | 
 | ||||||
| export default class SettingService { | export default class SettingService { | ||||||
|     private logger: Logger; |     private logger: Logger; | ||||||
| 
 | 
 | ||||||
|     private settingStore: ISettingStore; |     private settingStore: ISettingStore; | ||||||
| 
 | 
 | ||||||
|  |     private eventStore: IEventStore; | ||||||
|  | 
 | ||||||
|     constructor( |     constructor( | ||||||
|         { settingStore }: Pick<IUnleashStores, 'settingStore'>, |         { | ||||||
|  |             settingStore, | ||||||
|  |             eventStore, | ||||||
|  |         }: Pick<IUnleashStores, 'settingStore' | 'eventStore'>, | ||||||
|         { getLogger }: Pick<IUnleashConfig, 'getLogger'>, |         { getLogger }: Pick<IUnleashConfig, 'getLogger'>, | ||||||
|     ) { |     ) { | ||||||
|         this.logger = getLogger('services/setting-service.ts'); |         this.logger = getLogger('services/setting-service.ts'); | ||||||
|         this.settingStore = settingStore; |         this.settingStore = settingStore; | ||||||
|  |         this.eventStore = eventStore; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async get<T>(id: string): Promise<T> { |     async get<T>(id: string): Promise<T> { | ||||||
|         return this.settingStore.get(id); |         return this.settingStore.get(id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async insert(id: string, value: object): Promise<void> { |     async insert(id: string, value: object, createdBy: string): Promise<void> { | ||||||
|         return this.settingStore.insert(id, value); |         const exists = await this.settingStore.exists(id); | ||||||
|  |         if (exists) { | ||||||
|  |             await this.settingStore.updateRow(id, value); | ||||||
|  |             await this.eventStore.store( | ||||||
|  |                 new SettingUpdatedEvent({ | ||||||
|  |                     createdBy, | ||||||
|  |                     data: { id }, | ||||||
|  |                 }), | ||||||
|  |             ); | ||||||
|  |         } else { | ||||||
|  |             await this.settingStore.insert(id, value); | ||||||
|  |             await this.eventStore.store( | ||||||
|  |                 new SettingCreatedEvent({ | ||||||
|  |                     createdBy, | ||||||
|  |                     data: { id }, | ||||||
|  |                 }), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async delete(id: string): Promise<void> { |     async delete(id: string, createdBy: string): Promise<void> { | ||||||
|         return this.settingStore.delete(id); |         await this.settingStore.delete(id); | ||||||
|  |         await this.eventStore.store( | ||||||
|  |             new SettingDeletedEvent({ | ||||||
|  |                 createdBy, | ||||||
|  |                 data: { | ||||||
|  |                     id, | ||||||
|  |                 }, | ||||||
|  |             }), | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ import User from '../types/user'; | |||||||
| import FakeResetTokenStore from '../../test/fixtures/fake-reset-token-store'; | import FakeResetTokenStore from '../../test/fixtures/fake-reset-token-store'; | ||||||
| import SettingService from './setting-service'; | import SettingService from './setting-service'; | ||||||
| import FakeSettingStore from '../../test/fixtures/fake-setting-store'; | import FakeSettingStore from '../../test/fixtures/fake-setting-store'; | ||||||
|  | import FakeEventStore from '../../test/fixtures/fake-event-store'; | ||||||
| 
 | 
 | ||||||
| const config: IUnleashConfig = createTestConfig(); | const config: IUnleashConfig = createTestConfig(); | ||||||
| 
 | 
 | ||||||
| @ -31,7 +32,10 @@ test('Should create new user', async () => { | |||||||
|     const sessionService = new SessionService({ sessionStore }, config); |     const sessionService = new SessionService({ sessionStore }, config); | ||||||
|     const emailService = new EmailService(config.email, config.getLogger); |     const emailService = new EmailService(config.email, config.getLogger); | ||||||
|     const settingService = new SettingService( |     const settingService = new SettingService( | ||||||
|         { settingStore: new FakeSettingStore() }, |         { | ||||||
|  |             settingStore: new FakeSettingStore(), | ||||||
|  |             eventStore: new FakeEventStore(), | ||||||
|  |         }, | ||||||
|         config, |         config, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @ -71,7 +75,10 @@ test('Should create default user', async () => { | |||||||
|     const sessionStore = new FakeSessionStore(); |     const sessionStore = new FakeSessionStore(); | ||||||
|     const sessionService = new SessionService({ sessionStore }, config); |     const sessionService = new SessionService({ sessionStore }, config); | ||||||
|     const settingService = new SettingService( |     const settingService = new SettingService( | ||||||
|         { settingStore: new FakeSettingStore() }, |         { | ||||||
|  |             settingStore: new FakeSettingStore(), | ||||||
|  |             eventStore: new FakeEventStore(), | ||||||
|  |         }, | ||||||
|         config, |         config, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @ -103,7 +110,10 @@ test('Should be a valid password', async () => { | |||||||
|     const sessionStore = new FakeSessionStore(); |     const sessionStore = new FakeSessionStore(); | ||||||
|     const sessionService = new SessionService({ sessionStore }, config); |     const sessionService = new SessionService({ sessionStore }, config); | ||||||
|     const settingService = new SettingService( |     const settingService = new SettingService( | ||||||
|         { settingStore: new FakeSettingStore() }, |         { | ||||||
|  |             settingStore: new FakeSettingStore(), | ||||||
|  |             eventStore: new FakeEventStore(), | ||||||
|  |         }, | ||||||
|         config, |         config, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @ -133,7 +143,10 @@ test('Password must be at least 10 chars', async () => { | |||||||
|     const sessionStore = new FakeSessionStore(); |     const sessionStore = new FakeSessionStore(); | ||||||
|     const sessionService = new SessionService({ sessionStore }, config); |     const sessionService = new SessionService({ sessionStore }, config); | ||||||
|     const settingService = new SettingService( |     const settingService = new SettingService( | ||||||
|         { settingStore: new FakeSettingStore() }, |         { | ||||||
|  |             settingStore: new FakeSettingStore(), | ||||||
|  |             eventStore: new FakeEventStore(), | ||||||
|  |         }, | ||||||
|         config, |         config, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @ -165,7 +178,10 @@ test('The password must contain at least one uppercase letter.', async () => { | |||||||
|     const sessionStore = new FakeSessionStore(); |     const sessionStore = new FakeSessionStore(); | ||||||
|     const sessionService = new SessionService({ sessionStore }, config); |     const sessionService = new SessionService({ sessionStore }, config); | ||||||
|     const settingService = new SettingService( |     const settingService = new SettingService( | ||||||
|         { settingStore: new FakeSettingStore() }, |         { | ||||||
|  |             settingStore: new FakeSettingStore(), | ||||||
|  |             eventStore: new FakeEventStore(), | ||||||
|  |         }, | ||||||
|         config, |         config, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @ -199,7 +215,10 @@ test('The password must contain at least one number', async () => { | |||||||
|     const sessionStore = new FakeSessionStore(); |     const sessionStore = new FakeSessionStore(); | ||||||
|     const sessionService = new SessionService({ sessionStore }, config); |     const sessionService = new SessionService({ sessionStore }, config); | ||||||
|     const settingService = new SettingService( |     const settingService = new SettingService( | ||||||
|         { settingStore: new FakeSettingStore() }, |         { | ||||||
|  |             settingStore: new FakeSettingStore(), | ||||||
|  |             eventStore: new FakeEventStore(), | ||||||
|  |         }, | ||||||
|         config, |         config, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @ -232,7 +251,10 @@ test('The password must contain at least one special character', async () => { | |||||||
|     const sessionStore = new FakeSessionStore(); |     const sessionStore = new FakeSessionStore(); | ||||||
|     const sessionService = new SessionService({ sessionStore }, config); |     const sessionService = new SessionService({ sessionStore }, config); | ||||||
|     const settingService = new SettingService( |     const settingService = new SettingService( | ||||||
|         { settingStore: new FakeSettingStore() }, |         { | ||||||
|  |             settingStore: new FakeSettingStore(), | ||||||
|  |             eventStore: new FakeEventStore(), | ||||||
|  |         }, | ||||||
|         config, |         config, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @ -265,7 +287,10 @@ test('Should be a valid password with special chars', async () => { | |||||||
|     const sessionStore = new FakeSessionStore(); |     const sessionStore = new FakeSessionStore(); | ||||||
|     const sessionService = new SessionService({ sessionStore }, config); |     const sessionService = new SessionService({ sessionStore }, config); | ||||||
|     const settingService = new SettingService( |     const settingService = new SettingService( | ||||||
|         { settingStore: new FakeSettingStore() }, |         { | ||||||
|  |             settingStore: new FakeSettingStore(), | ||||||
|  |             eventStore: new FakeEventStore(), | ||||||
|  |         }, | ||||||
|         config, |         config, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -64,6 +64,9 @@ export const ENVIRONMENT_IMPORT = 'environment-import'; | |||||||
| export const SEGMENT_CREATED = 'segment-created'; | export const SEGMENT_CREATED = 'segment-created'; | ||||||
| export const SEGMENT_UPDATED = 'segment-updated'; | export const SEGMENT_UPDATED = 'segment-updated'; | ||||||
| export const SEGMENT_DELETED = 'segment-deleted'; | export const SEGMENT_DELETED = 'segment-deleted'; | ||||||
|  | export const SETTING_CREATED = 'setting-created'; | ||||||
|  | export const SETTING_UPDATED = 'setting-updated'; | ||||||
|  | export const SETTING_DELETED = 'setting-deleted'; | ||||||
| 
 | 
 | ||||||
| export const CLIENT_METRICS = 'client-metrics'; | export const CLIENT_METRICS = 'client-metrics'; | ||||||
| 
 | 
 | ||||||
| @ -437,3 +440,30 @@ export class ProjectUserUpdateRoleEvent extends BaseEvent { | |||||||
|         this.preData = preData; |         this.preData = preData; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export class SettingCreatedEvent extends BaseEvent { | ||||||
|  |     readonly data: any; | ||||||
|  | 
 | ||||||
|  |     constructor(eventData: { createdBy: string; data: any }) { | ||||||
|  |         super(SETTING_CREATED, eventData.createdBy); | ||||||
|  |         this.data = eventData.data; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class SettingDeletedEvent extends BaseEvent { | ||||||
|  |     readonly data: any; | ||||||
|  | 
 | ||||||
|  |     constructor(eventData: { createdBy: string; data: any }) { | ||||||
|  |         super(SETTING_DELETED, eventData.createdBy); | ||||||
|  |         this.data = eventData.data; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class SettingUpdatedEvent extends BaseEvent { | ||||||
|  |     readonly data: any; | ||||||
|  | 
 | ||||||
|  |     constructor(eventData: { createdBy: string; data: any }) { | ||||||
|  |         super(SETTING_UPDATED, eventData.createdBy); | ||||||
|  |         this.data = eventData.data; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ import SessionService from '../../../../lib/services/session-service'; | |||||||
| import { RoleName } from '../../../../lib/types/model'; | import { RoleName } from '../../../../lib/types/model'; | ||||||
| import SettingService from '../../../../lib/services/setting-service'; | import SettingService from '../../../../lib/services/setting-service'; | ||||||
| import FakeSettingStore from '../../../fixtures/fake-setting-store'; | import FakeSettingStore from '../../../fixtures/fake-setting-store'; | ||||||
|  | import FakeEventStore from '../../../fixtures/fake-event-store'; | ||||||
| 
 | 
 | ||||||
| let app; | let app; | ||||||
| let stores; | let stores; | ||||||
| @ -56,7 +57,10 @@ beforeAll(async () => { | |||||||
|     ); |     ); | ||||||
|     const sessionService = new SessionService({ sessionStore }, config); |     const sessionService = new SessionService({ sessionStore }, config); | ||||||
|     const settingService = new SettingService( |     const settingService = new SettingService( | ||||||
|         { settingStore: new FakeSettingStore() }, |         { | ||||||
|  |             settingStore: new FakeSettingStore(), | ||||||
|  |             eventStore: new FakeEventStore(), | ||||||
|  |         }, | ||||||
|         config, |         config, | ||||||
|     ); |     ); | ||||||
|     userService = new UserService(stores, config, { |     userService = new UserService(stores, config, { | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ import InvalidTokenError from '../../../lib/error/invalid-token-error'; | |||||||
| import { IUser } from '../../../lib/types/user'; | import { IUser } from '../../../lib/types/user'; | ||||||
| import SettingService from '../../../lib/services/setting-service'; | import SettingService from '../../../lib/services/setting-service'; | ||||||
| import FakeSettingStore from '../../fixtures/fake-setting-store'; | import FakeSettingStore from '../../fixtures/fake-setting-store'; | ||||||
|  | import FakeEventStore from '../../fixtures/fake-event-store'; | ||||||
| 
 | 
 | ||||||
| const config: IUnleashConfig = createTestConfig(); | const config: IUnleashConfig = createTestConfig(); | ||||||
| 
 | 
 | ||||||
| @ -31,7 +32,10 @@ beforeAll(async () => { | |||||||
|     sessionService = new SessionService(stores, config); |     sessionService = new SessionService(stores, config); | ||||||
|     const emailService = new EmailService(undefined, config.getLogger); |     const emailService = new EmailService(undefined, config.getLogger); | ||||||
|     const settingService = new SettingService( |     const settingService = new SettingService( | ||||||
|         { settingStore: new FakeSettingStore() }, |         { | ||||||
|  |             settingStore: new FakeSettingStore(), | ||||||
|  |             eventStore: new FakeEventStore(), | ||||||
|  |         }, | ||||||
|         config, |         config, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,11 @@ import SettingService from '../../../lib/services/setting-service'; | |||||||
| import { createTestConfig } from '../../config/test-config'; | import { createTestConfig } from '../../config/test-config'; | ||||||
| import dbInit from '../helpers/database-init'; | import dbInit from '../helpers/database-init'; | ||||||
| import { IUnleashStores } from '../../../lib/types/stores'; | import { IUnleashStores } from '../../../lib/types/stores'; | ||||||
|  | import { | ||||||
|  |     SETTING_CREATED, | ||||||
|  |     SETTING_DELETED, | ||||||
|  |     SETTING_UPDATED, | ||||||
|  | } from '../../../lib/types/events'; | ||||||
| 
 | 
 | ||||||
| let stores: IUnleashStores; | let stores: IUnleashStores; | ||||||
| let db; | let db; | ||||||
| @ -13,23 +18,51 @@ beforeAll(async () => { | |||||||
|     stores = db.stores; |     stores = db.stores; | ||||||
|     service = new SettingService(stores, config); |     service = new SettingService(stores, config); | ||||||
| }); | }); | ||||||
|  | beforeEach(async () => { | ||||||
|  |     await stores.eventStore.deleteAll(); | ||||||
|  | }); | ||||||
| afterAll(async () => { | afterAll(async () => { | ||||||
|     await db.destroy(); |     await db.destroy(); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test('Can create new setting', async () => { | test('Can create new setting', async () => { | ||||||
|     const someData = { some: 'blob' }; |     const someData = { some: 'blob' }; | ||||||
|     await service.insert('some-setting', someData); |     await service.insert('some-setting', someData, 'test-user'); | ||||||
|     const actual = await service.get('some-setting'); |     const actual = await service.get('some-setting'); | ||||||
| 
 | 
 | ||||||
|     expect(actual).toStrictEqual(someData); |     expect(actual).toStrictEqual(someData); | ||||||
|  |     const { eventStore } = stores; | ||||||
|  |     const createdEvents = await eventStore.getEventsFilterByType( | ||||||
|  |         SETTING_CREATED, | ||||||
|  |     ); | ||||||
|  |     expect(createdEvents).toHaveLength(1); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test('Can delete setting', async () => { | test('Can delete setting', async () => { | ||||||
|     const someData = { some: 'blob' }; |     const someData = { some: 'blob' }; | ||||||
|     await service.insert('some-setting', someData); |     await service.insert('some-setting', someData, 'test-user'); | ||||||
|     await service.delete('some-setting'); |     await service.delete('some-setting', 'test-user'); | ||||||
| 
 | 
 | ||||||
|     const actual = await service.get('some-setting'); |     const actual = await service.get('some-setting'); | ||||||
|     expect(actual).toBeUndefined(); |     expect(actual).toBeUndefined(); | ||||||
|  |     const { eventStore } = stores; | ||||||
|  |     const createdEvents = await eventStore.getEventsFilterByType( | ||||||
|  |         SETTING_DELETED, | ||||||
|  |     ); | ||||||
|  |     expect(createdEvents).toHaveLength(1); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test('Can update setting', async () => { | ||||||
|  |     const { eventStore } = stores; | ||||||
|  |     const someData = { some: 'blob' }; | ||||||
|  |     await service.insert('updated-setting', someData, 'test-user'); | ||||||
|  |     await service.insert( | ||||||
|  |         'updated-setting', | ||||||
|  |         { ...someData, test: 'fun' }, | ||||||
|  |         'test-user', | ||||||
|  |     ); | ||||||
|  |     const updatedEvents = await eventStore.getEventsFilterByType( | ||||||
|  |         SETTING_UPDATED, | ||||||
|  |     ); | ||||||
|  |     expect(updatedEvents).toHaveLength(1); | ||||||
| }); | }); | ||||||
|  | |||||||
| @ -1,6 +1,9 @@ | |||||||
| import { | import { | ||||||
|     APPLICATION_CREATED, |     APPLICATION_CREATED, | ||||||
|     FEATURE_CREATED, |     FEATURE_CREATED, | ||||||
|  |     FEATURE_DELETED, | ||||||
|  |     FeatureCreatedEvent, | ||||||
|  |     FeatureDeletedEvent, | ||||||
|     IEvent, |     IEvent, | ||||||
| } from '../../../lib/types/events'; | } from '../../../lib/types/events'; | ||||||
| 
 | 
 | ||||||
| @ -19,6 +22,9 @@ beforeAll(async () => { | |||||||
|     eventStore = stores.eventStore; |     eventStore = stores.eventStore; | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | beforeEach(async () => { | ||||||
|  |     await eventStore.deleteAll(); | ||||||
|  | }); | ||||||
| afterAll(async () => { | afterAll(async () => { | ||||||
|     if (db) { |     if (db) { | ||||||
|         await db.destroy(); |         await db.destroy(); | ||||||
| @ -140,6 +146,7 @@ test('Should delete stored event', async () => { | |||||||
|         }, |         }, | ||||||
|     }; |     }; | ||||||
|     await eventStore.store(event); |     await eventStore.store(event); | ||||||
|  |     await eventStore.store(event); | ||||||
|     const events = await eventStore.getAll(); |     const events = await eventStore.getAll(); | ||||||
|     const lastEvent = events[0]; |     const lastEvent = events[0]; | ||||||
|     await eventStore.delete(lastEvent.id); |     await eventStore.delete(lastEvent.id); | ||||||
| @ -178,3 +185,36 @@ test('Should delete all stored events', async () => { | |||||||
| 
 | 
 | ||||||
|     expect(events).toHaveLength(0); |     expect(events).toHaveLength(0); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | test('Should get all events of type', async () => { | ||||||
|  |     const data = { name: 'someName', project: 'test-project' }; | ||||||
|  |     await Promise.all( | ||||||
|  |         [0, 1, 2, 3, 4, 5].map(async (id) => { | ||||||
|  |             const event = | ||||||
|  |                 id % 2 == 0 | ||||||
|  |                     ? new FeatureCreatedEvent({ | ||||||
|  |                           project: data.project, | ||||||
|  |                           featureName: data.name, | ||||||
|  |                           createdBy: 'test-user', | ||||||
|  |                           data, | ||||||
|  |                           tags: [], | ||||||
|  |                       }) | ||||||
|  |                     : new FeatureDeletedEvent({ | ||||||
|  |                           project: data.project, | ||||||
|  |                           preData: data, | ||||||
|  |                           featureName: data.name, | ||||||
|  |                           createdBy: 'test-user', | ||||||
|  |                           tags: [], | ||||||
|  |                       }); | ||||||
|  |             return eventStore.store(event); | ||||||
|  |         }), | ||||||
|  |     ); | ||||||
|  |     const featureCreatedEvents = await eventStore.getEventsFilterByType( | ||||||
|  |         FEATURE_CREATED, | ||||||
|  |     ); | ||||||
|  |     expect(featureCreatedEvents).toHaveLength(3); | ||||||
|  |     const featureDeletedEvents = await eventStore.getEventsFilterByType( | ||||||
|  |         FEATURE_DELETED, | ||||||
|  |     ); | ||||||
|  |     expect(featureDeletedEvents).toHaveLength(3); | ||||||
|  | }); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user