mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +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