mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-18 01:18:23 +02:00
parent
01074fd437
commit
bd94f358c3
@ -3,7 +3,14 @@
|
|||||||
const { EventEmitter } = require('events');
|
const { EventEmitter } = require('events');
|
||||||
const { DROP_FEATURES } = require('../event-type');
|
const { DROP_FEATURES } = require('../event-type');
|
||||||
|
|
||||||
const EVENT_COLUMNS = ['id', 'type', 'created_by', 'created_at', 'data'];
|
const EVENT_COLUMNS = [
|
||||||
|
'id',
|
||||||
|
'type',
|
||||||
|
'created_by',
|
||||||
|
'created_at',
|
||||||
|
'data',
|
||||||
|
'tags',
|
||||||
|
];
|
||||||
|
|
||||||
class EventStore extends EventEmitter {
|
class EventStore extends EventEmitter {
|
||||||
constructor(db) {
|
constructor(db) {
|
||||||
@ -16,6 +23,7 @@ class EventStore extends EventEmitter {
|
|||||||
type: event.type,
|
type: event.type,
|
||||||
created_by: event.createdBy, // eslint-disable-line
|
created_by: event.createdBy, // eslint-disable-line
|
||||||
data: event.data,
|
data: event.data,
|
||||||
|
tags: event.tags ? JSON.stringify(event.tags) : [],
|
||||||
});
|
});
|
||||||
this.emit(event.type, event);
|
this.emit(event.type, event);
|
||||||
}
|
}
|
||||||
@ -56,6 +64,7 @@ class EventStore extends EventEmitter {
|
|||||||
createdBy: row.created_by,
|
createdBy: row.created_by,
|
||||||
createdAt: row.created_at,
|
createdAt: row.created_at,
|
||||||
data: row.data,
|
data: row.data,
|
||||||
|
tags: row.tags,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ test('should revive toggle', t => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should create event when reviving toggle', async t => {
|
test('should create event when reviving toggle', async t => {
|
||||||
t.plan(4);
|
t.plan(6);
|
||||||
const name = 'name1';
|
const name = 'name1';
|
||||||
const {
|
const {
|
||||||
request,
|
request,
|
||||||
@ -99,13 +99,24 @@ test('should create event when reviving toggle', async t => {
|
|||||||
strategies: [{ name: 'default' }],
|
strategies: [{ name: 'default' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await featureToggleService.addTag(
|
||||||
|
name,
|
||||||
|
{
|
||||||
|
type: 'simple',
|
||||||
|
value: 'tag',
|
||||||
|
},
|
||||||
|
'test@test.com',
|
||||||
|
);
|
||||||
|
|
||||||
await request.post(`${base}/api/admin/archive/revive/${name}`);
|
await request.post(`${base}/api/admin/archive/revive/${name}`);
|
||||||
|
|
||||||
const events = await eventStore.getEvents();
|
const events = await eventStore.getEvents();
|
||||||
t.is(events.length, 1);
|
t.is(events.length, 3);
|
||||||
t.is(events[0].type, 'feature-revived');
|
t.is(events[2].type, 'feature-revived');
|
||||||
t.is(events[0].data.name, name);
|
t.is(events[2].data.name, name);
|
||||||
t.is(events[0].createdBy, 'unknown');
|
t.is(events[2].createdBy, 'unknown');
|
||||||
|
t.is(events[2].tags[0].type, 'simple');
|
||||||
|
t.is(events[2].tags[0].value, 'tag');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should require toggle name when reviving', t => {
|
test('should require toggle name when reviving', t => {
|
||||||
|
@ -8,7 +8,11 @@ const { createServices } = require('../../services');
|
|||||||
const permissions = require('../../../test/fixtures/permissions');
|
const permissions = require('../../../test/fixtures/permissions');
|
||||||
const getLogger = require('../../../test/fixtures/no-logger');
|
const getLogger = require('../../../test/fixtures/no-logger');
|
||||||
const getApp = require('../../app');
|
const getApp = require('../../app');
|
||||||
const { UPDATE_FEATURE, CREATE_FEATURE } = require('../../permissions');
|
const {
|
||||||
|
UPDATE_FEATURE,
|
||||||
|
CREATE_FEATURE,
|
||||||
|
DELETE_FEATURE,
|
||||||
|
} = require('../../permissions');
|
||||||
|
|
||||||
const eventBus = new EventEmitter();
|
const eventBus = new EventEmitter();
|
||||||
|
|
||||||
@ -31,6 +35,7 @@ function getSetup() {
|
|||||||
base,
|
base,
|
||||||
perms,
|
perms,
|
||||||
featureToggleStore: stores.featureToggleStore,
|
featureToggleStore: stores.featureToggleStore,
|
||||||
|
eventStore: stores.eventStore,
|
||||||
request: supertest(app),
|
request: supertest(app),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -523,3 +528,55 @@ test('Should be able to filter on project', t => {
|
|||||||
t.is(res.body.features[1].name, 'a_tag.toggle');
|
t.is(res.body.features[1].name, 'a_tag.toggle');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Tags should be included in archive events', async t => {
|
||||||
|
const { request, eventStore, featureToggleStore, base, perms } = getSetup();
|
||||||
|
perms.withPermissions(UPDATE_FEATURE, DELETE_FEATURE);
|
||||||
|
|
||||||
|
featureToggleStore.createFeature({
|
||||||
|
name: 'a_team.toggle',
|
||||||
|
enabled: false,
|
||||||
|
strategies: [{ name: 'default' }],
|
||||||
|
project: 'projecta',
|
||||||
|
});
|
||||||
|
featureToggleStore.tagFeature('a_team.toggle', {
|
||||||
|
type: 'simple',
|
||||||
|
value: 'tag',
|
||||||
|
});
|
||||||
|
await request
|
||||||
|
.delete(`${base}/api/admin/features/a_team.toggle`)
|
||||||
|
.expect(200);
|
||||||
|
const events = await eventStore.getEvents();
|
||||||
|
t.is(events[0].type, 'feature-archived');
|
||||||
|
t.is(events[0].tags[0].type, 'simple');
|
||||||
|
t.is(events[0].tags[0].value, 'tag');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Tags should be included in updated events', async t => {
|
||||||
|
const { request, eventStore, featureToggleStore, base, perms } = getSetup();
|
||||||
|
perms.withPermissions(UPDATE_FEATURE, DELETE_FEATURE);
|
||||||
|
|
||||||
|
featureToggleStore.createFeature({
|
||||||
|
name: 'a_team.toggle',
|
||||||
|
enabled: false,
|
||||||
|
strategies: [{ name: 'default' }],
|
||||||
|
project: 'projecta',
|
||||||
|
});
|
||||||
|
featureToggleStore.tagFeature('a_team.toggle', {
|
||||||
|
type: 'simple',
|
||||||
|
value: 'tag',
|
||||||
|
});
|
||||||
|
await request
|
||||||
|
.put(`${base}/api/admin/features/a_team.toggle`)
|
||||||
|
.send({
|
||||||
|
name: 'a_team.toggle',
|
||||||
|
enabled: false,
|
||||||
|
strategies: [{ name: 'default' }],
|
||||||
|
project: 'projectb',
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
const events = await eventStore.getEvents();
|
||||||
|
t.is(events[0].type, 'feature-updated');
|
||||||
|
t.is(events[0].tags[0].type, 'simple');
|
||||||
|
t.is(events[0].tags[0].value, 'tag');
|
||||||
|
});
|
||||||
|
@ -75,29 +75,41 @@ class FeatureToggleService {
|
|||||||
await this.featureToggleStore.getFeature(updatedFeature.name);
|
await this.featureToggleStore.getFeature(updatedFeature.name);
|
||||||
const value = await featureSchema.validateAsync(updatedFeature);
|
const value = await featureSchema.validateAsync(updatedFeature);
|
||||||
await this.featureToggleStore.updateFeature(value);
|
await this.featureToggleStore.updateFeature(value);
|
||||||
|
const tags =
|
||||||
|
(await this.featureToggleStore.getAllTagsForFeature(
|
||||||
|
updatedFeature.name,
|
||||||
|
)) || [];
|
||||||
await this.eventStore.store({
|
await this.eventStore.store({
|
||||||
type: FEATURE_UPDATED,
|
type: FEATURE_UPDATED,
|
||||||
createdBy: userName,
|
createdBy: userName,
|
||||||
data: updatedFeature,
|
data: updatedFeature,
|
||||||
|
tags,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async archiveToggle(name, userName) {
|
async archiveToggle(name, userName) {
|
||||||
await this.featureToggleStore.getFeature(name);
|
await this.featureToggleStore.getFeature(name);
|
||||||
await this.featureToggleStore.archiveFeature(name);
|
await this.featureToggleStore.archiveFeature(name);
|
||||||
|
const tags =
|
||||||
|
(await this.featureToggleStore.getAllTagsForFeature(name)) || [];
|
||||||
await this.eventStore.store({
|
await this.eventStore.store({
|
||||||
type: FEATURE_ARCHIVED,
|
type: FEATURE_ARCHIVED,
|
||||||
createdBy: userName,
|
createdBy: userName,
|
||||||
data: { name },
|
data: { name },
|
||||||
|
tags,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async reviveToggle(name, userName) {
|
async reviveToggle(name, userName) {
|
||||||
await this.featureToggleStore.reviveFeature({ name });
|
await this.featureToggleStore.reviveFeature({ name });
|
||||||
|
const tags =
|
||||||
|
(await this.featureToggleStore.getAllTagsForFeature(name)) || [];
|
||||||
|
|
||||||
await this.eventStore.store({
|
await this.eventStore.store({
|
||||||
type: FEATURE_REVIVED,
|
type: FEATURE_REVIVED,
|
||||||
createdBy: userName,
|
createdBy: userName,
|
||||||
data: { name },
|
data: { name },
|
||||||
|
tags,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,10 +193,15 @@ class FeatureToggleService {
|
|||||||
const feature = await this.featureToggleStore.getFeature(featureName);
|
const feature = await this.featureToggleStore.getFeature(featureName);
|
||||||
feature[field] = value;
|
feature[field] = value;
|
||||||
await this.featureToggleStore.updateFeature(feature);
|
await this.featureToggleStore.updateFeature(feature);
|
||||||
|
const tags =
|
||||||
|
(await this.featureToggleStore.getAllTagsForFeature(featureName)) ||
|
||||||
|
[];
|
||||||
|
|
||||||
await this.eventStore.store({
|
await this.eventStore.store({
|
||||||
type: FEATURE_UPDATED,
|
type: FEATURE_UPDATED,
|
||||||
createdBy: userName,
|
createdBy: userName,
|
||||||
data: feature,
|
data: feature,
|
||||||
|
tags,
|
||||||
});
|
});
|
||||||
return feature;
|
return feature;
|
||||||
}
|
}
|
||||||
|
18
migrations/20210127094440-add-tags-column-to-events.js
Normal file
18
migrations/20210127094440-add-tags-column-to-events.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
exports.up = function(db, cb) {
|
||||||
|
db.runSql(
|
||||||
|
`
|
||||||
|
ALTER TABLE events ADD COLUMN IF NOT EXISTS tags json DEFAULT '[]'
|
||||||
|
`,
|
||||||
|
cb,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
exports.down = function(db, cb) {
|
||||||
|
db.runSql(
|
||||||
|
`
|
||||||
|
ALTER TABLE events DROP COLUMN IF EXISTS tags;
|
||||||
|
`,
|
||||||
|
cb,
|
||||||
|
);
|
||||||
|
};
|
7
test/fixtures/fake-feature-toggle-store.js
vendored
7
test/fixtures/fake-feature-toggle-store.js
vendored
@ -31,6 +31,13 @@ module.exports = () => {
|
|||||||
);
|
);
|
||||||
_features.push(updatedFeature);
|
_features.push(updatedFeature);
|
||||||
},
|
},
|
||||||
|
archiveFeature: feature => {
|
||||||
|
_features.slice(
|
||||||
|
_features.indexOf(({ name }) => name === feature.name),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
_archive.push(feature);
|
||||||
|
},
|
||||||
createFeature: feature => _features.push(feature),
|
createFeature: feature => _features.push(feature),
|
||||||
getArchivedFeatures: () => Promise.resolve(_archive),
|
getArchivedFeatures: () => Promise.resolve(_archive),
|
||||||
addArchivedFeature: feature => _archive.push(feature),
|
addArchivedFeature: feature => _archive.push(feature),
|
||||||
|
Loading…
Reference in New Issue
Block a user