mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +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)
|
||||
.from(TABLE)
|
||||
.limit(100)
|
||||
.whereRaw("data ->> 'name' = ?", [name])
|
||||
.where('type', name)
|
||||
.andWhere(
|
||||
'id',
|
||||
'>=',
|
||||
|
@ -2,30 +2,67 @@ import { IUnleashConfig } from '../types/option';
|
||||
import { IUnleashStores } from '../types/stores';
|
||||
import { Logger } from '../logger';
|
||||
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 {
|
||||
private logger: Logger;
|
||||
|
||||
private settingStore: ISettingStore;
|
||||
|
||||
private eventStore: IEventStore;
|
||||
|
||||
constructor(
|
||||
{ settingStore }: Pick<IUnleashStores, 'settingStore'>,
|
||||
{
|
||||
settingStore,
|
||||
eventStore,
|
||||
}: Pick<IUnleashStores, 'settingStore' | 'eventStore'>,
|
||||
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
||||
) {
|
||||
this.logger = getLogger('services/setting-service.ts');
|
||||
this.settingStore = settingStore;
|
||||
this.eventStore = eventStore;
|
||||
}
|
||||
|
||||
async get<T>(id: string): Promise<T> {
|
||||
return this.settingStore.get(id);
|
||||
}
|
||||
|
||||
async insert(id: string, value: object): Promise<void> {
|
||||
return this.settingStore.insert(id, value);
|
||||
async insert(id: string, value: object, createdBy: string): Promise<void> {
|
||||
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> {
|
||||
return this.settingStore.delete(id);
|
||||
async delete(id: string, createdBy: string): Promise<void> {
|
||||
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 SettingService from './setting-service';
|
||||
import FakeSettingStore from '../../test/fixtures/fake-setting-store';
|
||||
import FakeEventStore from '../../test/fixtures/fake-event-store';
|
||||
|
||||
const config: IUnleashConfig = createTestConfig();
|
||||
|
||||
@ -31,7 +32,10 @@ test('Should create new user', async () => {
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const emailService = new EmailService(config.email, config.getLogger);
|
||||
const settingService = new SettingService(
|
||||
{ settingStore: new FakeSettingStore() },
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
eventStore: new FakeEventStore(),
|
||||
},
|
||||
config,
|
||||
);
|
||||
|
||||
@ -71,7 +75,10 @@ test('Should create default user', async () => {
|
||||
const sessionStore = new FakeSessionStore();
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const settingService = new SettingService(
|
||||
{ settingStore: new FakeSettingStore() },
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
eventStore: new FakeEventStore(),
|
||||
},
|
||||
config,
|
||||
);
|
||||
|
||||
@ -103,7 +110,10 @@ test('Should be a valid password', async () => {
|
||||
const sessionStore = new FakeSessionStore();
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const settingService = new SettingService(
|
||||
{ settingStore: new FakeSettingStore() },
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
eventStore: new FakeEventStore(),
|
||||
},
|
||||
config,
|
||||
);
|
||||
|
||||
@ -133,7 +143,10 @@ test('Password must be at least 10 chars', async () => {
|
||||
const sessionStore = new FakeSessionStore();
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const settingService = new SettingService(
|
||||
{ settingStore: new FakeSettingStore() },
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
eventStore: new FakeEventStore(),
|
||||
},
|
||||
config,
|
||||
);
|
||||
|
||||
@ -165,7 +178,10 @@ test('The password must contain at least one uppercase letter.', async () => {
|
||||
const sessionStore = new FakeSessionStore();
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const settingService = new SettingService(
|
||||
{ settingStore: new FakeSettingStore() },
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
eventStore: new FakeEventStore(),
|
||||
},
|
||||
config,
|
||||
);
|
||||
|
||||
@ -199,7 +215,10 @@ test('The password must contain at least one number', async () => {
|
||||
const sessionStore = new FakeSessionStore();
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const settingService = new SettingService(
|
||||
{ settingStore: new FakeSettingStore() },
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
eventStore: new FakeEventStore(),
|
||||
},
|
||||
config,
|
||||
);
|
||||
|
||||
@ -232,7 +251,10 @@ test('The password must contain at least one special character', async () => {
|
||||
const sessionStore = new FakeSessionStore();
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const settingService = new SettingService(
|
||||
{ settingStore: new FakeSettingStore() },
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
eventStore: new FakeEventStore(),
|
||||
},
|
||||
config,
|
||||
);
|
||||
|
||||
@ -265,7 +287,10 @@ test('Should be a valid password with special chars', async () => {
|
||||
const sessionStore = new FakeSessionStore();
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const settingService = new SettingService(
|
||||
{ settingStore: new FakeSettingStore() },
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
eventStore: new FakeEventStore(),
|
||||
},
|
||||
config,
|
||||
);
|
||||
|
||||
|
@ -64,6 +64,9 @@ export const ENVIRONMENT_IMPORT = 'environment-import';
|
||||
export const SEGMENT_CREATED = 'segment-created';
|
||||
export const SEGMENT_UPDATED = 'segment-updated';
|
||||
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';
|
||||
|
||||
@ -437,3 +440,30 @@ export class ProjectUserUpdateRoleEvent extends BaseEvent {
|
||||
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 SettingService from '../../../../lib/services/setting-service';
|
||||
import FakeSettingStore from '../../../fixtures/fake-setting-store';
|
||||
import FakeEventStore from '../../../fixtures/fake-event-store';
|
||||
|
||||
let app;
|
||||
let stores;
|
||||
@ -56,7 +57,10 @@ beforeAll(async () => {
|
||||
);
|
||||
const sessionService = new SessionService({ sessionStore }, config);
|
||||
const settingService = new SettingService(
|
||||
{ settingStore: new FakeSettingStore() },
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
eventStore: new FakeEventStore(),
|
||||
},
|
||||
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 SettingService from '../../../lib/services/setting-service';
|
||||
import FakeSettingStore from '../../fixtures/fake-setting-store';
|
||||
import FakeEventStore from '../../fixtures/fake-event-store';
|
||||
|
||||
const config: IUnleashConfig = createTestConfig();
|
||||
|
||||
@ -31,7 +32,10 @@ beforeAll(async () => {
|
||||
sessionService = new SessionService(stores, config);
|
||||
const emailService = new EmailService(undefined, config.getLogger);
|
||||
const settingService = new SettingService(
|
||||
{ settingStore: new FakeSettingStore() },
|
||||
{
|
||||
settingStore: new FakeSettingStore(),
|
||||
eventStore: new FakeEventStore(),
|
||||
},
|
||||
config,
|
||||
);
|
||||
|
||||
|
@ -2,6 +2,11 @@ import SettingService from '../../../lib/services/setting-service';
|
||||
import { createTestConfig } from '../../config/test-config';
|
||||
import dbInit from '../helpers/database-init';
|
||||
import { IUnleashStores } from '../../../lib/types/stores';
|
||||
import {
|
||||
SETTING_CREATED,
|
||||
SETTING_DELETED,
|
||||
SETTING_UPDATED,
|
||||
} from '../../../lib/types/events';
|
||||
|
||||
let stores: IUnleashStores;
|
||||
let db;
|
||||
@ -13,23 +18,51 @@ beforeAll(async () => {
|
||||
stores = db.stores;
|
||||
service = new SettingService(stores, config);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await stores.eventStore.deleteAll();
|
||||
});
|
||||
afterAll(async () => {
|
||||
await db.destroy();
|
||||
});
|
||||
|
||||
test('Can create new setting', async () => {
|
||||
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');
|
||||
|
||||
expect(actual).toStrictEqual(someData);
|
||||
const { eventStore } = stores;
|
||||
const createdEvents = await eventStore.getEventsFilterByType(
|
||||
SETTING_CREATED,
|
||||
);
|
||||
expect(createdEvents).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('Can delete setting', async () => {
|
||||
const someData = { some: 'blob' };
|
||||
await service.insert('some-setting', someData);
|
||||
await service.delete('some-setting');
|
||||
await service.insert('some-setting', someData, 'test-user');
|
||||
await service.delete('some-setting', 'test-user');
|
||||
|
||||
const actual = await service.get('some-setting');
|
||||
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 {
|
||||
APPLICATION_CREATED,
|
||||
FEATURE_CREATED,
|
||||
FEATURE_DELETED,
|
||||
FeatureCreatedEvent,
|
||||
FeatureDeletedEvent,
|
||||
IEvent,
|
||||
} from '../../../lib/types/events';
|
||||
|
||||
@ -19,6 +22,9 @@ beforeAll(async () => {
|
||||
eventStore = stores.eventStore;
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await eventStore.deleteAll();
|
||||
});
|
||||
afterAll(async () => {
|
||||
if (db) {
|
||||
await db.destroy();
|
||||
@ -140,6 +146,7 @@ test('Should delete stored event', async () => {
|
||||
},
|
||||
};
|
||||
await eventStore.store(event);
|
||||
await eventStore.store(event);
|
||||
const events = await eventStore.getAll();
|
||||
const lastEvent = events[0];
|
||||
await eventStore.delete(lastEvent.id);
|
||||
@ -178,3 +185,36 @@ test('Should delete all stored events', async () => {
|
||||
|
||||
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