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:
parent
37d6c4886a
commit
68d4ac0252
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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 });
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -197,6 +197,8 @@ export interface IAddonConfig {
|
||||
export interface ICreateEvent {
|
||||
type: string;
|
||||
createdBy: string;
|
||||
project?: string;
|
||||
environment?: string;
|
||||
data?: any;
|
||||
tags?: ITag[];
|
||||
}
|
||||
|
@ -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[]>;
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
};
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
4
src/test/fixtures/fake-event-store.ts
vendored
4
src/test/fixtures/fake-event-store.ts
vendored
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user