1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-19 01:17:18 +02:00

feat: add project and environment columns to events (#942)

* feat: add project and environment columns to events

* Added events for feature_strategy update

* fix duplicate test key for dbInit

* Fix argument list for toggleService calls in tests
This commit is contained in:
Christopher Kolstad 2021-09-20 12:13:38 +02:00 committed by GitHub
parent 37d6c4886a
commit f85f66d4f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 241 additions and 37 deletions

View File

@ -12,6 +12,8 @@ const EVENT_COLUMNS = [
'created_at', 'created_at',
'data', 'data',
'tags', 'tags',
'project',
'environment',
]; ];
export interface IEventTable { export interface IEventTable {
@ -20,6 +22,8 @@ export interface IEventTable {
created_by: string; created_by: string;
created_at: Date; created_at: Date;
data: any; data: any;
project?: string;
environment?: string;
tags: []; tags: [];
} }
@ -126,6 +130,19 @@ class EventStore extends EventEmitter implements IEventStore {
} }
} }
async getEventsFilterByProject(project: string): Promise<IEvent[]> {
try {
const rows = await this.db
.select(EVENT_COLUMNS)
.from(TABLE)
.where({ project })
.orderBy('created_at', 'desc');
return rows.map(this.rowToEvent);
} catch (err) {
return [];
}
}
rowToEvent(row: IEventTable): IEvent { rowToEvent(row: IEventTable): IEvent {
return { return {
id: row.id, id: row.id,
@ -134,6 +151,8 @@ class EventStore extends EventEmitter implements IEventStore {
createdAt: row.created_at, createdAt: row.created_at,
data: row.data, data: row.data,
tags: row.tags || [], tags: row.tags || [],
project: row.project,
environment: row.environment,
}; };
} }
@ -143,6 +162,8 @@ class EventStore extends EventEmitter implements IEventStore {
created_by: e.createdBy, created_by: e.createdBy,
data: e.data, data: e.data,
tags: JSON.stringify(e.tags), tags: JSON.stringify(e.tags),
project: e.project,
environment: e.environment,
}; };
} }
} }

View File

@ -24,7 +24,14 @@ export default class EventController extends Controller {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
async getEvents(req, res): Promise<void> { async getEvents(req, res): Promise<void> {
const events = await this.eventService.getEvents(); let events;
if (req.query?.project) {
events = await this.eventService.getEventsForProject(
req.query.project,
);
} else {
events = await this.eventService.getEvents();
}
eventDiffer.addDiffs(events); eventDiffer.addDiffs(events);
res.json({ version, events }); res.json({ version, events });
} }

View File

@ -174,6 +174,7 @@ class FeatureController extends Controller {
s, s,
createdFeature.project, createdFeature.project,
createdFeature.name, createdFeature.name,
userName,
), ),
), ),
); );
@ -219,6 +220,7 @@ class FeatureController extends Controller {
s, s,
projectId, projectId,
featureName, featureName,
userName,
), ),
), ),
); );

View File

@ -220,14 +220,16 @@ export default class ProjectFeaturesController extends Controller {
} }
async addStrategy( async addStrategy(
req: Request<FeatureStrategyParams, any, IStrategyConfig, any>, req: IAuthRequest<FeatureStrategyParams, any, IStrategyConfig, any>,
res: Response, res: Response,
): Promise<void> { ): Promise<void> {
const { projectId, featureName, environment } = req.params; const { projectId, featureName, environment } = req.params;
const userName = extractUsername(req);
const featureStrategy = await this.featureService.createStrategy( const featureStrategy = await this.featureService.createStrategy(
req.body, req.body,
projectId, projectId,
featureName, featureName,
userName,
environment, environment,
); );
res.status(200).json(featureStrategy); res.status(200).json(featureStrategy);
@ -248,34 +250,42 @@ export default class ProjectFeaturesController extends Controller {
} }
async updateStrategy( async updateStrategy(
req: Request<StrategyIdParams, any, StrategyUpdateBody, any>, req: IAuthRequest<StrategyIdParams, any, StrategyUpdateBody, any>,
res: Response, res: Response,
): Promise<void> { ): Promise<void> {
const { strategyId } = req.params; const { strategyId, environment, projectId } = req.params;
const userName = extractUsername(req);
const updatedStrategy = await this.featureService.updateStrategy( const updatedStrategy = await this.featureService.updateStrategy(
strategyId, strategyId,
environment,
projectId,
userName,
req.body, req.body,
); );
res.status(200).json(updatedStrategy); res.status(200).json(updatedStrategy);
} }
async patchStrategy( async patchStrategy(
req: Request<StrategyIdParams, any, Operation[], any>, req: IAuthRequest<StrategyIdParams, any, Operation[], any>,
res: Response, res: Response,
): Promise<void> { ): Promise<void> {
const { strategyId } = req.params; const { strategyId, projectId, environment } = req.params;
const userName = extractUsername(req);
const patch = req.body; const patch = req.body;
const strategy = await this.featureService.getStrategy(strategyId); const strategy = await this.featureService.getStrategy(strategyId);
const { newDocument } = applyPatch(strategy, patch); const { newDocument } = applyPatch(strategy, patch);
const updatedStrategy = await this.featureService.updateStrategy( const updatedStrategy = await this.featureService.updateStrategy(
strategyId, strategyId,
environment,
projectId,
userName,
newDocument, newDocument,
); );
res.status(200).json(updatedStrategy); res.status(200).json(updatedStrategy);
} }
async getStrategy( async getStrategy(
req: Request<StrategyIdParams, any, any, any>, req: IAuthRequest<StrategyIdParams, any, any, any>,
res: Response, res: Response,
): Promise<void> { ): Promise<void> {
this.logger.info('Getting strategy'); this.logger.info('Getting strategy');
@ -286,18 +296,25 @@ export default class ProjectFeaturesController extends Controller {
} }
async deleteStrategy( async deleteStrategy(
req: Request<StrategyIdParams, any, any, any>, req: IAuthRequest<StrategyIdParams, any, any, any>,
res: Response, res: Response,
): Promise<void> { ): Promise<void> {
this.logger.info('Deleting strategy'); this.logger.info('Deleting strategy');
const { environment, projectId } = req.params;
const userName = extractUsername(req);
const { strategyId } = req.params; const { strategyId } = req.params;
this.logger.info(strategyId); this.logger.info(strategyId);
const strategy = await this.featureService.deleteStrategy(strategyId); const strategy = await this.featureService.deleteStrategy(
strategyId,
userName,
projectId,
environment,
);
res.status(200).json(strategy); res.status(200).json(strategy);
} }
async updateStrategyParameter( async updateStrategyParameter(
req: Request< req: IAuthRequest<
StrategyIdParams, StrategyIdParams,
any, any,
{ name: string; value: string | number }, { name: string; value: string | number },
@ -305,7 +322,8 @@ export default class ProjectFeaturesController extends Controller {
>, >,
res: Response, res: Response,
): Promise<void> { ): Promise<void> {
const { strategyId } = req.params; const { strategyId, environment, projectId } = req.params;
const userName = extractUsername(req);
const { name, value } = req.body; const { name, value } = req.body;
const updatedStrategy = const updatedStrategy =
@ -313,6 +331,9 @@ export default class ProjectFeaturesController extends Controller {
strategyId, strategyId,
name, name,
value, value,
userName,
projectId,
environment,
); );
res.status(200).json(updatedStrategy); res.status(200).json(updatedStrategy);
} }

View File

@ -28,6 +28,10 @@ export default class EventService {
(e: IEvent) => e.type !== FEATURE_METADATA_UPDATED, (e: IEvent) => e.type !== FEATURE_METADATA_UPDATED,
); );
} }
async getEventsForProject(project: string): Promise<IEvent[]> {
return this.eventStore.getEventsFilterByProject(project);
}
} }
module.exports = EventService; module.exports = EventService;

View File

@ -10,11 +10,14 @@ import {
FEATURE_ARCHIVED, FEATURE_ARCHIVED,
FEATURE_CREATED, FEATURE_CREATED,
FEATURE_DELETED, FEATURE_DELETED,
FEATURE_METADATA_UPDATED,
FEATURE_REVIVED, FEATURE_REVIVED,
FEATURE_STALE_OFF, FEATURE_STALE_OFF,
FEATURE_STALE_ON, FEATURE_STALE_ON,
FEATURE_STRATEGY_ADD,
FEATURE_STRATEGY_REMOVE,
FEATURE_STRATEGY_UPDATE,
FEATURE_UPDATED, FEATURE_UPDATED,
FEATURE_METADATA_UPDATED,
} from '../types/events'; } from '../types/events';
import { GLOBAL_ENV } from '../types/environment'; import { GLOBAL_ENV } from '../types/environment';
import NotFoundError from '../error/notfound-error'; import NotFoundError from '../error/notfound-error';
@ -88,16 +91,11 @@ class FeatureToggleServiceV2 {
this.featureEnvironmentStore = featureEnvironmentStore; this.featureEnvironmentStore = featureEnvironmentStore;
} }
/*
TODO after 4.1.0 release:
- add FEATURE_STRATEGY_ADD event
- add FEATURE_STRATEGY_REMOVE event
- add FEATURE_STRATEGY_UPDATE event
*/
async createStrategy( async createStrategy(
strategyConfig: Omit<IStrategyConfig, 'id'>, strategyConfig: Omit<IStrategyConfig, 'id'>,
projectId: string, projectId: string,
featureName: string, featureName: string,
userName: string,
environment: string = GLOBAL_ENV, environment: string = GLOBAL_ENV,
): Promise<IStrategyConfig> { ): Promise<IStrategyConfig> {
try { try {
@ -111,12 +109,20 @@ class FeatureToggleServiceV2 {
featureName, featureName,
environment, environment,
}); });
return { const data = {
id: newFeatureStrategy.id, id: newFeatureStrategy.id,
name: newFeatureStrategy.strategyName, name: newFeatureStrategy.strategyName,
constraints: newFeatureStrategy.constraints, constraints: newFeatureStrategy.constraints,
parameters: newFeatureStrategy.parameters, parameters: newFeatureStrategy.parameters,
}; };
await this.eventStore.store({
type: FEATURE_STRATEGY_ADD,
project: projectId,
createdBy: userName,
environment,
data,
});
return data;
} catch (e) { } catch (e) {
if (e.code === FOREIGN_KEY_VIOLATION) { if (e.code === FOREIGN_KEY_VIOLATION) {
throw new BadDataError( throw new BadDataError(
@ -126,6 +132,12 @@ class FeatureToggleServiceV2 {
throw e; throw e;
} }
} }
/*
TODO after 4.1.0 release:
- add FEATURE_STRATEGY_ADD event
- add FEATURE_STRATEGY_REMOVE event
- add FEATURE_STRATEGY_UPDATE event
*/
/** /**
* PUT /api/admin/projects/:projectId/features/:featureName/strategies/:strategyId ? * PUT /api/admin/projects/:projectId/features/:featureName/strategies/:strategyId ?
@ -139,6 +151,9 @@ class FeatureToggleServiceV2 {
// TODO: verify projectId is not changed from URL! // TODO: verify projectId is not changed from URL!
async updateStrategy( async updateStrategy(
id: string, id: string,
environment: string,
project: string,
userName: string,
updates: Partial<IFeatureStrategy>, updates: Partial<IFeatureStrategy>,
): Promise<IStrategyConfig> { ): Promise<IStrategyConfig> {
const existingStrategy = await this.featureStrategiesStore.get(id); const existingStrategy = await this.featureStrategiesStore.get(id);
@ -147,12 +162,20 @@ class FeatureToggleServiceV2 {
id, id,
updates, updates,
); );
return { const data = {
id: strategy.id, id: strategy.id,
name: strategy.strategyName, name: strategy.strategyName,
constraints: strategy.constraints || [], constraints: strategy.constraints || [],
parameters: strategy.parameters, parameters: strategy.parameters,
}; };
await this.eventStore.store({
type: FEATURE_STRATEGY_UPDATE,
project,
environment,
createdBy: userName,
data,
});
return data;
} }
throw new NotFoundError(`Could not find strategy with id ${id}`); throw new NotFoundError(`Could not find strategy with id ${id}`);
} }
@ -162,6 +185,9 @@ class FeatureToggleServiceV2 {
id: string, id: string,
name: string, name: string,
value: string | number, value: string | number,
userName: string,
project: string,
environment: string,
): Promise<IStrategyConfig> { ): Promise<IStrategyConfig> {
const existingStrategy = await this.featureStrategiesStore.get(id); const existingStrategy = await this.featureStrategiesStore.get(id);
if (existingStrategy.id === id) { if (existingStrategy.id === id) {
@ -170,12 +196,20 @@ class FeatureToggleServiceV2 {
id, id,
existingStrategy, existingStrategy,
); );
return { const data = {
id: strategy.id, id: strategy.id,
name: strategy.strategyName, name: strategy.strategyName,
constraints: strategy.constraints || [], constraints: strategy.constraints || [],
parameters: strategy.parameters, parameters: strategy.parameters,
}; };
await this.eventStore.store({
type: FEATURE_STRATEGY_UPDATE,
project,
environment,
createdBy: userName,
data,
});
return data;
} }
throw new NotFoundError(`Could not find strategy with id ${id}`); throw new NotFoundError(`Could not find strategy with id ${id}`);
} }
@ -188,8 +222,22 @@ class FeatureToggleServiceV2 {
* @param id * @param id
* @param updates * @param updates
*/ */
async deleteStrategy(id: string): Promise<void> { async deleteStrategy(
return this.featureStrategiesStore.delete(id); id: string,
userName: string,
project: string = 'default',
environment: string = GLOBAL_ENV,
): Promise<void> {
await this.featureStrategiesStore.delete(id);
await this.eventStore.store({
type: FEATURE_STRATEGY_REMOVE,
project,
environment,
createdBy: userName,
data: {
id,
},
});
} }
async getStrategiesForEnvironment( async getStrategiesForEnvironment(
@ -309,6 +357,7 @@ class FeatureToggleServiceV2 {
await this.eventStore.store({ await this.eventStore.store({
type: FEATURE_CREATED, type: FEATURE_CREATED,
createdBy: userName, createdBy: userName,
project: projectId,
data, data,
}); });
@ -341,6 +390,7 @@ class FeatureToggleServiceV2 {
type: FEATURE_METADATA_UPDATED, type: FEATURE_METADATA_UPDATED,
createdBy: userName, createdBy: userName,
data: featureToggle, data: featureToggle,
project: projectId,
tags, tags,
}); });
return featureToggle; return featureToggle;
@ -455,12 +505,13 @@ class FeatureToggleServiceV2 {
createdBy: userName, createdBy: userName,
data, data,
tags, tags,
project: feature.project,
}); });
return feature; return feature;
} }
async archiveToggle(name: string, userName: string): Promise<void> { async archiveToggle(name: string, userName: string): Promise<void> {
await this.featureToggleStore.get(name); const feature = await this.featureToggleStore.get(name);
await this.featureToggleStore.archive(name); await this.featureToggleStore.archive(name);
const tags = const tags =
(await this.featureTagStore.getAllTagsForFeature(name)) || []; (await this.featureTagStore.getAllTagsForFeature(name)) || [];
@ -468,6 +519,7 @@ class FeatureToggleServiceV2 {
type: FEATURE_ARCHIVED, type: FEATURE_ARCHIVED,
createdBy: userName, createdBy: userName,
data: { name }, data: { name },
project: feature.project,
tags, tags,
}); });
} }
@ -514,6 +566,8 @@ class FeatureToggleServiceV2 {
createdBy: userName, createdBy: userName,
data, data,
tags, tags,
project: projectId,
environment,
}); });
return feature; return feature;
} }
@ -583,6 +637,7 @@ class FeatureToggleServiceV2 {
type: event || FEATURE_UPDATED, type: event || FEATURE_UPDATED,
createdBy: userName, createdBy: userName,
data, data,
project: data.project,
tags, tags,
}); });
return feature; return feature;
@ -612,6 +667,7 @@ class FeatureToggleServiceV2 {
type: FEATURE_REVIVED, type: FEATURE_REVIVED,
createdBy: userName, createdBy: userName,
data, data,
project: data.project,
tags, tags,
}); });
} }

View File

@ -131,6 +131,7 @@ export default class ProjectService {
type: PROJECT_CREATED, type: PROJECT_CREATED,
createdBy: getCreatedBy(user), createdBy: getCreatedBy(user),
data, data,
project: newProject.id,
}); });
return data; return data;
@ -146,6 +147,7 @@ export default class ProjectService {
type: PROJECT_UPDATED, type: PROJECT_UPDATED,
createdBy: getCreatedBy(user), createdBy: getCreatedBy(user),
data: project, data: project,
project: project.id,
}); });
} }
@ -211,10 +213,11 @@ export default class ProjectService {
await this.eventStore.store({ await this.eventStore.store({
type: PROJECT_DELETED, type: PROJECT_DELETED,
createdBy: getCreatedBy(user), createdBy: getCreatedBy(user),
project: id,
data: { id }, data: { id },
}); });
this.accessService.removeDefaultProjectRoles(user, id); await this.accessService.removeDefaultProjectRoles(user, id);
} }
async validateId(id: string): Promise<boolean> { async validateId(id: string): Promise<boolean> {

View File

@ -9,6 +9,9 @@ export const FEATURE_REVIVED = 'feature-revived';
export const FEATURE_IMPORT = 'feature-import'; export const FEATURE_IMPORT = 'feature-import';
export const FEATURE_TAGGED = 'feature-tagged'; export const FEATURE_TAGGED = 'feature-tagged';
export const FEATURE_TAG_IMPORT = 'feature-tag-import'; export const FEATURE_TAG_IMPORT = 'feature-tag-import';
export const FEATURE_STRATEGY_UPDATE = 'feature-strategy-update';
export const FEATURE_STRATEGY_ADD = 'feature-strategy-add';
export const FEATURE_STRATEGY_REMOVE = 'feature-strategy-remove';
export const DROP_FEATURE_TAGS = 'drop-feature-tags'; export const DROP_FEATURE_TAGS = 'drop-feature-tags';
export const FEATURE_UNTAGGED = 'feature-untagged'; export const FEATURE_UNTAGGED = 'feature-untagged';
export const FEATURE_STALE_ON = 'feature-stale-on'; export const FEATURE_STALE_ON = 'feature-stale-on';

View File

@ -197,6 +197,8 @@ export interface IAddonConfig {
export interface ICreateEvent { export interface ICreateEvent {
type: string; type: string;
createdBy: string; createdBy: string;
project?: string;
environment?: string;
data?: any; data?: any;
tags?: ITag[]; tags?: ITag[];
} }

View File

@ -7,4 +7,5 @@ export interface IEventStore extends Store<IEvent, number>, EventEmitter {
batchStore(events: ICreateEvent[]): Promise<void>; batchStore(events: ICreateEvent[]): Promise<void>;
getEvents(): Promise<IEvent[]>; getEvents(): Promise<IEvent[]>;
getEventsFilterByType(name: string): Promise<IEvent[]>; getEventsFilterByType(name: string): Promise<IEvent[]>;
getEventsFilterByProject(project: string): Promise<IEvent[]>;
} }

View File

@ -0,0 +1,29 @@
'use strict';
exports.up = function (db, cb) {
db.runSql(
`
ALTER TABLE events
ADD COLUMN project TEXT;
ALTER TABLE events
ADD COLUMN environment TEXT;
CREATE INDEX events_project_idx ON events(project);
CREATE INDEX events_environment_idx ON events(environment);
`,
cb,
);
};
exports.down = function (db, cb) {
db.runSql(
`
DROP INDEX events_environment_idx;
DROP INDEX events_project_idx;
ALTER TABLE events
DROP COLUMN environment;
ALTER TABLE events
DROP COLUMN project;
`,
cb,
);
};

View File

@ -1,13 +1,17 @@
import { setupApp } from '../../helpers/test-helper'; import { IUnleashTest, setupApp } from '../../helpers/test-helper';
import dbInit from '../../helpers/database-init'; import dbInit, { ITestDb } from '../../helpers/database-init';
import getLogger from '../../../fixtures/no-logger'; import getLogger from '../../../fixtures/no-logger';
import { FEATURE_CREATED } from '../../../../lib/types/events';
import { IEventStore } from '../../../../lib/types/stores/event-store';
let app; let app: IUnleashTest;
let db; let db: ITestDb;
let eventStore: IEventStore;
beforeAll(async () => { beforeAll(async () => {
db = await dbInit('event_api_serial', getLogger); db = await dbInit('event_api_serial', getLogger);
app = await setupApp(db.stores); app = await setupApp(db.stores);
eventStore = db.stores.eventStore;
}); });
afterAll(async () => { afterAll(async () => {
@ -30,3 +34,29 @@ test('returns events given a name', async () => {
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200); .expect(200);
}); });
test('Can filter by project', async () => {
await eventStore.store({
type: FEATURE_CREATED,
project: 'something-else',
data: { id: 'some-other-feature' },
tags: [],
createdBy: 'test-user',
environment: 'test',
});
await eventStore.store({
type: FEATURE_CREATED,
project: 'default',
data: { id: 'feature' },
tags: [],
createdBy: 'test-user',
environment: 'test',
});
await app.request
.get('/api/admin/events?project=default')
.expect(200)
.expect((res) => {
expect(res.body.events).toHaveLength(1);
expect(res.body.events[0].data.id).toEqual('feature');
});
});

View File

@ -32,6 +32,7 @@ beforeAll(async () => {
strategy, strategy,
projectId, projectId,
toggle.name, toggle.name,
username,
); );
}; };
@ -273,6 +274,7 @@ test('can not toggle of feature that does not exist', async () => {
test('can toggle a feature that does exist', async () => { test('can toggle a feature that does exist', async () => {
expect.assertions(0); expect.assertions(0);
const featureName = 'existing.feature'; const featureName = 'existing.feature';
const username = 'toggle-feature';
const feature = const feature =
await app.services.featureToggleServiceV2.createFeatureToggle( await app.services.featureToggleServiceV2.createFeatureToggle(
'default', 'default',
@ -285,6 +287,7 @@ test('can toggle a feature that does exist', async () => {
defaultStrategy, defaultStrategy,
'default', 'default',
featureName, featureName,
username,
); );
return app.request return app.request
.post(`/api/admin/features/${feature.name}/toggle`) .post(`/api/admin/features/${feature.name}/toggle`)

View File

@ -57,6 +57,7 @@ beforeAll(async () => {
}, },
project, project,
feature1, feature1,
username,
); );
await featureToggleServiceV2.createStrategy( await featureToggleServiceV2.createStrategy(
{ {
@ -66,6 +67,7 @@ beforeAll(async () => {
}, },
project, project,
feature1, feature1,
username,
environment, environment,
); );
@ -85,6 +87,7 @@ beforeAll(async () => {
}, },
project, project,
feature2, feature2,
username,
environment, environment,
); );
@ -104,6 +107,7 @@ beforeAll(async () => {
}, },
project2, project2,
feature3, feature3,
username,
environment, environment,
); );
}); });
@ -135,7 +139,7 @@ test('returns feature toggle with :global: config', async () => {
}); });
}); });
test('returns feature toggle with :global: config', async () => { test('returns feature toggle with testing environment config', async () => {
const token = await apiTokenService.createApiToken({ const token = await apiTokenService.createApiToken({
type: ApiTokenType.CLIENT, type: ApiTokenType.CLIENT,
username, username,

View File

@ -27,6 +27,8 @@ afterAll(async () => {
}); });
test('Should create feature toggle strategy configuration', async () => { test('Should create feature toggle strategy configuration', async () => {
const projectId = 'default';
const username = 'feature-toggle';
const config: Omit<IStrategyConfig, 'id'> = { const config: Omit<IStrategyConfig, 'id'> = {
name: 'default', name: 'default',
constraints: [], constraints: [],
@ -43,8 +45,9 @@ test('Should create feature toggle strategy configuration', async () => {
const createdConfig = await service.createStrategy( const createdConfig = await service.createStrategy(
config, config,
'default', projectId,
'Demo', 'Demo',
username,
); );
expect(createdConfig.name).toEqual('default'); expect(createdConfig.name).toEqual('default');
@ -52,6 +55,8 @@ test('Should create feature toggle strategy configuration', async () => {
}); });
test('Should be able to update existing strategy configuration', async () => { test('Should be able to update existing strategy configuration', async () => {
const projectId = 'default';
const username = 'existing-strategy';
const config: Omit<IStrategyConfig, 'id'> = { const config: Omit<IStrategyConfig, 'id'> = {
name: 'default', name: 'default',
constraints: [], constraints: [],
@ -59,7 +64,7 @@ test('Should be able to update existing strategy configuration', async () => {
}; };
await service.createFeatureToggle( await service.createFeatureToggle(
'default', projectId,
{ {
name: 'update-existing-strategy', name: 'update-existing-strategy',
}, },
@ -70,11 +75,18 @@ test('Should be able to update existing strategy configuration', async () => {
config, config,
'default', 'default',
'update-existing-strategy', 'update-existing-strategy',
username,
); );
expect(createdConfig.name).toEqual('default'); expect(createdConfig.name).toEqual('default');
const updatedConfig = await service.updateStrategy(createdConfig.id, { const updatedConfig = await service.updateStrategy(
parameters: { b2b: true }, createdConfig.id,
}); GLOBAL_ENV,
projectId,
username,
{
parameters: { b2b: true },
},
);
expect(createdConfig.id).toEqual(updatedConfig.id); expect(createdConfig.id).toEqual(updatedConfig.id);
expect(updatedConfig.parameters).toEqual({ b2b: true }); expect(updatedConfig.parameters).toEqual({ b2b: true });
}); });
@ -96,7 +108,7 @@ test('Should include legacy props in event log when updating strategy configurat
userName, userName,
); );
await service.createStrategy(config, 'default', featureName); await service.createStrategy(config, 'default', featureName, userName);
await service.updateEnabled( await service.updateEnabled(
'default', 'default',
featureName, featureName,
@ -112,6 +124,7 @@ test('Should include legacy props in event log when updating strategy configurat
}); });
test('Should be able to get strategy by id', async () => { test('Should be able to get strategy by id', async () => {
const userName = 'strategy';
const config: Omit<IStrategyConfig, 'id'> = { const config: Omit<IStrategyConfig, 'id'> = {
name: 'default', name: 'default',
constraints: [], constraints: [],
@ -130,6 +143,7 @@ test('Should be able to get strategy by id', async () => {
config, config,
'default', 'default',
'Demo', 'Demo',
userName,
); );
const fetchedConfig = await service.getStrategy(createdConfig.id); const fetchedConfig = await service.getStrategy(createdConfig.id);
expect(fetchedConfig).toEqual(createdConfig); expect(fetchedConfig).toEqual(createdConfig);

View File

@ -10,7 +10,7 @@ let userStore: IUserStore;
let currentUser; let currentUser;
beforeAll(async () => { beforeAll(async () => {
db = await dbInit('project_store_serial', getLogger); db = await dbInit('user_feedback_store', getLogger);
stores = db.stores; stores = db.stores;
userFeedbackStore = stores.userFeedbackStore; userFeedbackStore = stores.userFeedbackStore;
userStore = stores.userStore; userStore = stores.userStore;

View File

@ -57,6 +57,10 @@ class FakeEventStore extends EventEmitter implements IEventStore {
async getEventsFilterByType(type: string): Promise<IEvent[]> { async getEventsFilterByType(type: string): Promise<IEvent[]> {
return this.events.filter((e) => e.type === type); return this.events.filter((e) => e.type === type);
} }
async getEventsFilterByProject(project: string): Promise<IEvent[]> {
return this.events.filter((e) => e.project === project);
}
} }
module.exports = FakeEventStore; module.exports = FakeEventStore;