1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-05 17:53:12 +02:00

feat: add project and environment columns to events

This commit is contained in:
Christopher Kolstad 2021-09-15 15:00:14 +02:00
parent 37d6c4886a
commit 68d4ac0252
No known key found for this signature in database
GPG Key ID: 559ACB0E3DB5538A
10 changed files with 115 additions and 7 deletions

View File

@ -12,6 +12,8 @@ const EVENT_COLUMNS = [
'created_at',
'data',
'tags',
'project',
'environment',
];
export interface IEventTable {
@ -20,6 +22,8 @@ export interface IEventTable {
created_by: string;
created_at: Date;
data: any;
project?: string;
environment?: string;
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 {
return {
id: row.id,
@ -134,6 +151,8 @@ class EventStore extends EventEmitter implements IEventStore {
createdAt: row.created_at,
data: row.data,
tags: row.tags || [],
project: row.project,
environment: row.environment,
};
}
@ -143,6 +162,8 @@ class EventStore extends EventEmitter implements IEventStore {
created_by: e.createdBy,
data: e.data,
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
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);
res.json({ version, events });
}

View File

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

View File

@ -309,6 +309,7 @@ class FeatureToggleServiceV2 {
await this.eventStore.store({
type: FEATURE_CREATED,
createdBy: userName,
project: projectId,
data,
});
@ -341,6 +342,7 @@ class FeatureToggleServiceV2 {
type: FEATURE_METADATA_UPDATED,
createdBy: userName,
data: featureToggle,
project: projectId,
tags,
});
return featureToggle;
@ -455,12 +457,13 @@ class FeatureToggleServiceV2 {
createdBy: userName,
data,
tags,
project: feature.project,
});
return feature;
}
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);
const tags =
(await this.featureTagStore.getAllTagsForFeature(name)) || [];
@ -468,6 +471,7 @@ class FeatureToggleServiceV2 {
type: FEATURE_ARCHIVED,
createdBy: userName,
data: { name },
project: feature.project,
tags,
});
}
@ -514,6 +518,7 @@ class FeatureToggleServiceV2 {
createdBy: userName,
data,
tags,
project: projectId,
});
return feature;
}
@ -583,6 +588,7 @@ class FeatureToggleServiceV2 {
type: event || FEATURE_UPDATED,
createdBy: userName,
data,
project: data.project,
tags,
});
return feature;
@ -612,6 +618,7 @@ class FeatureToggleServiceV2 {
type: FEATURE_REVIVED,
createdBy: userName,
data,
project: data.project,
tags,
});
}

View File

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

View File

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

View File

@ -7,4 +7,5 @@ export interface IEventStore extends Store<IEvent, number>, EventEmitter {
batchStore(events: ICreateEvent[]): Promise<void>;
getEvents(): 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 dbInit from '../../helpers/database-init';
import { IUnleashTest, setupApp } from '../../helpers/test-helper';
import dbInit, { ITestDb } from '../../helpers/database-init';
import getLogger from '../../../fixtures/no-logger';
import { FEATURE_CREATED } from '../../../../lib/types/events';
import { IEventStore } from '../../../../lib/types/stores/event-store';
let app;
let db;
let app: IUnleashTest;
let db: ITestDb;
let eventStore: IEventStore;
beforeAll(async () => {
db = await dbInit('event_api_serial', getLogger);
app = await setupApp(db.stores);
eventStore = db.stores.eventStore;
});
afterAll(async () => {
@ -30,3 +34,29 @@ test('returns events given a name', async () => {
.expect('Content-Type', /json/)
.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

@ -57,6 +57,10 @@ class FakeEventStore extends EventEmitter implements IEventStore {
async getEventsFilterByType(type: string): Promise<IEvent[]> {
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;