1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-01 00:08:27 +01:00

cleanup api-db-stores a bit

This commit is contained in:
ivaosthu 2016-11-05 10:16:48 +01:00
parent 23bf2250fc
commit 4037fdf8ba
11 changed files with 142 additions and 149 deletions

View File

@ -1,36 +1,45 @@
'use strict'; 'use strict';
const { EventEmitter } = require('events');
const EVENT_COLUMNS = ['id', 'type', 'created_by', 'created_at', 'data']; const EVENT_COLUMNS = ['id', 'type', 'created_by', 'created_at', 'data'];
module.exports = function (db) { class EventStore extends EventEmitter {
function storeEvent (event) {
return db('events').insert({ constructor (db) {
super();
this.db = db;
}
store (event) {
return this.db('events').insert({
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,
}); })
.then(() => this.emit(event.type, event));
} }
function getEvents () { getEvents () {
return db return this.db
.select(EVENT_COLUMNS) .select(EVENT_COLUMNS)
.from('events') .from('events')
.limit(100) .limit(100)
.orderBy('created_at', 'desc') .orderBy('created_at', 'desc')
.map(rowToEvent); .map(this.rowToEvent);
} }
function getEventsFilterByName (name) { getEventsFilterByName (name) {
return db return this.db
.select(EVENT_COLUMNS) .select(EVENT_COLUMNS)
.from('events') .from('events')
.limit(100) .limit(100)
.whereRaw('data ->> \'name\' = ?', [name]) .whereRaw('data ->> \'name\' = ?', [name])
.orderBy('created_at', 'desc') .orderBy('created_at', 'desc')
.map(rowToEvent); .map(this.rowToEvent);
} }
function rowToEvent (row) { rowToEvent (row) {
return { return {
id: row.id, id: row.id,
type: row.type, type: row.type,
@ -39,11 +48,7 @@ module.exports = function (db) {
data: row.data, data: row.data,
}; };
} }
return {
store: storeEvent,
getEvents,
getEventsFilterByName,
};
}; };
module.exports = EventStore;

View File

@ -5,42 +5,43 @@ const logger = require('../logger');
const NotFoundError = require('../error/notfound-error'); const NotFoundError = require('../error/notfound-error');
const FEATURE_COLUMNS = ['name', 'description', 'enabled', 'strategies']; const FEATURE_COLUMNS = ['name', 'description', 'enabled', 'strategies'];
module.exports = function (db, eventStore) { class FeatureToggleStore {
eventStore.on(eventType.featureCreated, event => createFeature(event.data)); constructor (db, eventStore) {
this.db = db;
eventStore.on(eventType.featureCreated, event => this._createFeature(event.data));
eventStore.on(eventType.featureUpdated, event => this._updateFeature(event.data));
eventStore.on(eventType.featureArchived, event => this._archiveFeature(event.data));
eventStore.on(eventType.featureRevived, event => this._reviveFeature(event.data));
}
eventStore.on(eventType.featureUpdated, event => updateFeature(event.data)); getFeatures () {
return this.db
eventStore.on(eventType.featureArchived, event => archiveFeature(event.data));
eventStore.on(eventType.featureRevived, event => reviveFeature(event.data));
function getFeatures () {
return db
.select(FEATURE_COLUMNS) .select(FEATURE_COLUMNS)
.from('features') .from('features')
.where({ archived: 0 }) .where({ archived: 0 })
.orderBy('name', 'asc') .orderBy('name', 'asc')
.map(rowToFeature); .map(this.rowToFeature);
} }
function getFeature (name) { getFeature (name) {
return db return this.db
.first(FEATURE_COLUMNS) .first(FEATURE_COLUMNS)
.from('features') .from('features')
.where({ name }) .where({ name })
.then(rowToFeature); .then(this.rowToFeature);
} }
function getArchivedFeatures () { getArchivedFeatures () {
return db return this.db
.select(FEATURE_COLUMNS) .select(FEATURE_COLUMNS)
.from('features') .from('features')
.where({ archived: 1 }) .where({ archived: 1 })
.orderBy('name', 'asc') .orderBy('name', 'asc')
.map(rowToFeature); .map(this.rowToFeature);
} }
function rowToFeature (row) { rowToFeature (row) {
if (!row) { if (!row) {
throw new NotFoundError('No feature toggle found'); throw new NotFoundError('No feature toggle found');
} }
@ -52,7 +53,7 @@ module.exports = function (db, eventStore) {
}; };
} }
function eventDataToRow (data) { eventDataToRow (data) {
return { return {
name: data.name, name: data.name,
description: data.description, description: data.description,
@ -62,25 +63,25 @@ module.exports = function (db, eventStore) {
}; };
} }
function createFeature (data) { _createFeature (data) {
return db('features') return this.db('features')
.insert(eventDataToRow(data)) .insert(this.eventDataToRow(data))
.catch(err => { .catch(err => {
logger.error('Could not insert feature, error was: ', err); logger.error('Could not insert feature, error was: ', err);
}); });
} }
function updateFeature (data) { _updateFeature (data) {
return db('features') return this.db('features')
.where({ name: data.name }) .where({ name: data.name })
.update(eventDataToRow(data)) .update(this.eventDataToRow(data))
.catch(err => { .catch(err => {
logger.error('Could not update feature, error was: ', err); logger.error('Could not update feature, error was: ', err);
}); });
} }
function archiveFeature (data) { _archiveFeature (data) {
return db('features') return this.db('features')
.where({ name: data.name }) .where({ name: data.name })
.update({ archived: 1 }) .update({ archived: 1 })
.catch(err => { .catch(err => {
@ -88,21 +89,14 @@ module.exports = function (db, eventStore) {
}); });
} }
function reviveFeature (data) { _reviveFeature (data) {
return db('features') return this.db('features')
.where({ name: data.name }) .where({ name: data.name })
.update({ archived: 0, enabled: 0 }) .update({ archived: 0, enabled: 0 })
.catch(err => { .catch(err => {
logger.error('Could not archive feature, error was: ', err); logger.error('Could not archive feature, error was: ', err);
}); });
} }
return {
getFeatures,
getFeature,
getArchivedFeatures,
_createFeature: createFeature, // visible for testing
_updateFeature: updateFeature, // visible for testing
};
}; };
module.exports = FeatureToggleStore;

View File

@ -1,13 +1,21 @@
'use strict'; 'use strict';
const eventDbCreator = require('./event'); const EventStore = require('./event-store');
const FeatureToggleStore = require('./feature-toggle-store');
const StrategyStore = require('./strategy-store');
const clientInstancesDbCreator = require('./client-instances'); const clientInstancesDbCreator = require('./client-instances');
const clientMetricsDbCreator = require('./client-metrics'); const clientMetricsDbCreator = require('./client-metrics');
const clientStrategiesDbCreator = require('./client-strategies'); const clientStrategiesDbCreator = require('./client-strategies');
module.exports = (db) => ({ module.exports = (db) => {
eventDb: eventDbCreator(db), const eventStore = new EventStore(db);
clientInstancesDb: clientInstancesDbCreator(db),
clientMetricsDb: clientMetricsDbCreator(db), return {
clientStrategiesDb: clientStrategiesDbCreator(db), eventStore,
}); featureToggleStore: new FeatureToggleStore(db, eventStore),
strategyStore: new StrategyStore(db, eventStore),
clientInstancesDb: clientInstancesDbCreator(db),
clientMetricsDb: clientMetricsDbCreator(db),
clientStrategiesDb: clientStrategiesDbCreator(db),
};
};

View File

@ -4,36 +4,39 @@ const eventType = require('../event-type');
const logger = require('../logger'); const logger = require('../logger');
const NotFoundError = require('../error/notfound-error'); const NotFoundError = require('../error/notfound-error');
const STRATEGY_COLUMNS = ['name', 'description', 'parameters_template']; const STRATEGY_COLUMNS = ['name', 'description', 'parameters_template'];
const TABLE = 'strategies';
module.exports = function (db, eventStore) { class StrategyStore {
eventStore.on(eventType.strategyCreated, event => createStrategy(event.data)); constructor (db, eventStore) {
this.db = db;
eventStore.on(eventType.strategyCreated, event => this._createStrategy(event.data));
eventStore.on(eventType.strategyDeleted, event => {
db(TABLE)
.where('name', event.data.name)
.del()
.catch(err => {
logger.error('Could not delete strategy, error was: ', err);
});
});
}
eventStore.on(eventType.strategyDeleted, event => { getStrategies () {
db('strategies') return this.db
.where('name', event.data.name)
.del()
.catch(err => {
logger.error('Could not delete strategy, error was: ', err);
});
});
function getStrategies () {
return db
.select(STRATEGY_COLUMNS) .select(STRATEGY_COLUMNS)
.from('strategies') .from(TABLE)
.orderBy('created_at', 'asc') .orderBy('created_at', 'asc')
.map(rowToStrategy); .map(this.rowToStrategy);
} }
function getStrategy (name) { getStrategy (name) {
return db return this.db
.first(STRATEGY_COLUMNS) .first(STRATEGY_COLUMNS)
.from('strategies') .from(TABLE)
.where({ name }) .where({ name })
.then(rowToStrategy); .then(this.rowToStrategy);
} }
function rowToStrategy (row) { rowToStrategy (row) {
if (!row) { if (!row) {
throw new NotFoundError('No strategy found'); throw new NotFoundError('No strategy found');
} }
@ -45,7 +48,7 @@ module.exports = function (db, eventStore) {
}; };
} }
function eventDataToRow (data) { eventDataToRow (data) {
return { return {
name: data.name, name: data.name,
description: data.description, description: data.description,
@ -53,18 +56,14 @@ module.exports = function (db, eventStore) {
}; };
} }
function createStrategy (data) { _createStrategy (data) {
db('strategies') this.db(TABLE)
.insert(eventDataToRow(data)) .insert(this.eventDataToRow(data))
.catch(err => { .catch(err => {
logger.error('Could not insert strategy, error was: ', err); logger.error('Could not insert strategy, error was: ', err);
}); });
} }
return {
getStrategies,
getStrategy,
_createStrategy: createStrategy, // visible for testing
};
}; };
module.exports = StrategyStore;

View File

@ -1,15 +0,0 @@
'use strict';
const EventEmitter = require('events').EventEmitter;
module.exports = class EventStore extends EventEmitter {
constructor (eventDb) {
super();
this.eventDb = eventDb;
}
create (event) {
return this.eventDb.store(event).then(() => this.emit(event.type, event));
}
};

View File

@ -4,17 +4,17 @@ const eventDiffer = require('../event-differ');
const version = 1; const version = 1;
module.exports = function (app, config) { module.exports = function (app, config) {
const eventDb = config.eventDb; const eventStore = config.eventStore;
app.get('/events', (req, res) => { app.get('/events', (req, res) => {
eventDb.getEvents().then(events => { eventStore.getEvents().then(events => {
eventDiffer.addDiffs(events); eventDiffer.addDiffs(events);
res.json({ version, events }); res.json({ version, events });
}); });
}); });
app.get('/events/:name', (req, res) => { app.get('/events/:name', (req, res) => {
eventDb.getEventsFilterByName(req.params.name).then(events => { eventStore.getEventsFilterByName(req.params.name).then(events => {
if (events) { if (events) {
eventDiffer.addDiffs(events); eventDiffer.addDiffs(events);
res.json(events); res.json(events);

View File

@ -6,11 +6,11 @@ const ValidationError = require('../error/validation-error');
const validateRequest = require('../error/validate-request'); const validateRequest = require('../error/validate-request');
module.exports = function (app, config) { module.exports = function (app, config) {
const featureDb = config.featureDb; const featureToggleStore = config.featureToggleStore;
const eventStore = config.eventStore; const eventStore = config.eventStore;
app.get('/archive/features', (req, res) => { app.get('/archive/features', (req, res) => {
featureDb.getArchivedFeatures().then(archivedFeatures => { featureToggleStore.getArchivedFeatures().then(archivedFeatures => {
res.json({ features: archivedFeatures }); res.json({ features: archivedFeatures });
}); });
}); });
@ -19,7 +19,7 @@ module.exports = function (app, config) {
req.checkBody('name', 'Name is required').notEmpty(); req.checkBody('name', 'Name is required').notEmpty();
validateRequest(req) validateRequest(req)
.then(() => eventStore.create({ .then(() => eventStore.store({
type: eventType.featureRevived, type: eventType.featureRevived,
createdBy: req.connection.remoteAddress, createdBy: req.connection.remoteAddress,
data: req.body, data: req.body,

View File

@ -13,17 +13,17 @@ const legacyFeatureMapper = require('../helper/legacy-feature-mapper');
const version = 1; const version = 1;
module.exports = function (app, config) { module.exports = function (app, config) {
const featureDb = config.featureDb; const featureToggleStore = config.featureToggleStore;
const eventStore = config.eventStore; const eventStore = config.eventStore;
app.get('/features', (req, res) => { app.get('/features', (req, res) => {
featureDb.getFeatures() featureToggleStore.getFeatures()
.then((features) => features.map(legacyFeatureMapper.addOldFields)) .then((features) => features.map(legacyFeatureMapper.addOldFields))
.then(features => res.json({ version, features })); .then(features => res.json({ version, features }));
}); });
app.get('/features/:featureName', (req, res) => { app.get('/features/:featureName', (req, res) => {
featureDb.getFeature(req.params.featureName) featureToggleStore.getFeature(req.params.featureName)
.then(legacyFeatureMapper.addOldFields) .then(legacyFeatureMapper.addOldFields)
.then(feature => res.json(feature).end()) .then(feature => res.json(feature).end())
.catch(() => res.status(404).json({ error: 'Could not find feature' })); .catch(() => res.status(404).json({ error: 'Could not find feature' }));
@ -36,7 +36,7 @@ module.exports = function (app, config) {
validateRequest(req) validateRequest(req)
.then(validateFormat) .then(validateFormat)
.then(validateUniqueName) .then(validateUniqueName)
.then(() => eventStore.create({ .then(() => eventStore.store({
type: eventType.featureCreated, type: eventType.featureCreated,
createdBy: extractUser(req), createdBy: extractUser(req),
data: legacyFeatureMapper.toNewFormat(req.body), data: legacyFeatureMapper.toNewFormat(req.body),
@ -61,8 +61,8 @@ module.exports = function (app, config) {
updatedFeature.name = featureName; updatedFeature.name = featureName;
featureDb.getFeature(featureName) featureToggleStore.getFeature(featureName)
.then(() => eventStore.create({ .then(() => eventStore.store({
type: eventType.featureUpdated, type: eventType.featureUpdated,
createdBy: userName, createdBy: userName,
data: updatedFeature, data: updatedFeature,
@ -79,8 +79,8 @@ module.exports = function (app, config) {
const featureName = req.params.featureName; const featureName = req.params.featureName;
const userName = extractUser(req); const userName = extractUser(req);
featureDb.getFeature(featureName) featureToggleStore.getFeature(featureName)
.then(() => eventStore.create({ .then(() => eventStore.store({
type: eventType.featureArchived, type: eventType.featureArchived,
createdBy: userName, createdBy: userName,
data: { data: {
@ -97,7 +97,7 @@ module.exports = function (app, config) {
function validateUniqueName (req) { function validateUniqueName (req) {
return new BPromise((resolve, reject) => { return new BPromise((resolve, reject) => {
featureDb.getFeature(req.body.name) featureToggleStore.getFeature(req.body.name)
.then(() => { .then(() => {
reject(new NameExistsError('Feature name already exist')); reject(new NameExistsError('Feature name already exist'));
}, () => { }, () => {

View File

@ -11,17 +11,17 @@ const extractUser = require('../extract-user');
const version = 1; const version = 1;
module.exports = function (app, config) { module.exports = function (app, config) {
const strategyDb = config.strategyDb; const strategyStore = config.strategyStore;
const eventStore = config.eventStore; const eventStore = config.eventStore;
app.get('/strategies', (req, res) => { app.get('/strategies', (req, res) => {
strategyDb.getStrategies().then(strategies => { strategyStore.getStrategies().then(strategies => {
res.json({ version, strategies }); res.json({ version, strategies });
}); });
}); });
app.get('/strategies/:name', (req, res) => { app.get('/strategies/:name', (req, res) => {
strategyDb.getStrategy(req.params.name) strategyStore.getStrategy(req.params.name)
.then(strategy => { .then(strategy => {
res.json(strategy); res.json(strategy);
}) })
@ -33,8 +33,8 @@ module.exports = function (app, config) {
app.delete('/strategies/:name', (req, res) => { app.delete('/strategies/:name', (req, res) => {
const strategyName = req.params.name; const strategyName = req.params.name;
strategyDb.getStrategy(strategyName) strategyStore.getStrategy(strategyName)
.then(() => eventStore.create({ .then(() => eventStore.store({
type: eventType.strategyDeleted, type: eventType.strategyDeleted,
createdBy: extractUser(req), createdBy: extractUser(req),
data: { data: {
@ -61,7 +61,7 @@ module.exports = function (app, config) {
validateRequest(req) validateRequest(req)
.then(validateStrategyName) .then(validateStrategyName)
.then(() => eventStore.create({ .then(() => eventStore.store({
type: eventType.strategyCreated, type: eventType.strategyCreated,
createdBy: extractUser(req), createdBy: extractUser(req),
data: newStrategy, data: newStrategy,
@ -81,7 +81,7 @@ module.exports = function (app, config) {
function validateStrategyName (req) { function validateStrategyName (req) {
return new BPromise((resolve, reject) => { return new BPromise((resolve, reject) => {
strategyDb.getStrategy(req.body.name) strategyStore.getStrategy(req.body.name)
.then(() => { .then(() => {
reject(new NameExistsError('Feature name already exist')); reject(new NameExistsError('Feature name already exist'));
}, () => { }, () => {

View File

@ -13,23 +13,23 @@ function createApp (options) {
const db = require('./lib/db/db-pool')(options.databaseUri); const db = require('./lib/db/db-pool')(options.databaseUri);
// Database dependecies (statefull) // Database dependecies (statefull)
const { eventDb, clientInstancesDb, clientMetricsDb, clientStrategiesDb } = require('./lib/db')(db); const {
eventStore,
// Needs some cleanup! featureToggleStore,
const EventStore = require('./lib/event-store'); strategyStore,
const eventStore = new EventStore(eventDb); clientInstancesDb,
const featureDb = require('./lib/db/feature')(db, eventStore); clientMetricsDb,
const strategyDb = require('./lib/db/strategy')(db, eventStore); clientStrategiesDb,
} = require('./lib/db')(db);
const config = { const config = {
baseUriPath: options.baseUriPath, baseUriPath: options.baseUriPath,
port: options.port, port: options.port,
publicFolder: options.publicFolder, publicFolder: options.publicFolder,
db, db,
eventDb,
eventStore, eventStore,
featureDb, featureToggleStore,
strategyDb, strategyStore,
clientMetricsDb, clientMetricsDb,
clientStrategiesDb, clientStrategiesDb,
clientInstancesDb, clientInstancesDb,

View File

@ -6,20 +6,22 @@ const BPromise = require('bluebird');
let request = require('supertest'); let request = require('supertest');
const databaseUri = require('./database-config').getDatabaseUri(); const databaseUri = require('./database-config').getDatabaseUri();
const knex = require('../../lib/db/db-pool')(databaseUri); const knex = require('../../lib/db/db-pool')(databaseUri);
const { eventDb, clientInstancesDb, clientStrategiesDb, clientMetricsDb } = require('../../lib/db')(knex); const {
const EventStore = require('../../lib/event-store'); eventStore,
const eventStore = new EventStore(eventDb); featureToggleStore,
const featureDb = require('../../lib/db/feature')(knex, eventStore); strategyStore,
const strategyDb = require('../../lib/db/strategy')(knex, eventStore); clientInstancesDb,
clientStrategiesDb,
clientMetricsDb,
} = require('../../lib/db')(knex);
const app = require('../../app')({ const app = require('../../app')({
baseUriPath: '', baseUriPath: '',
db: knex, db: knex,
eventDb,
eventStore, eventStore,
featureDb, featureToggleStore,
strategyDb, strategyStore,
clientStrategiesDb, clientStrategiesDb,
clientInstancesDb, clientInstancesDb,
clientMetricsDb, clientMetricsDb,
@ -42,7 +44,7 @@ function createStrategies () {
emails: 'String', emails: 'String',
}, },
}, },
], strategy => strategyDb._createStrategy(strategy)); ], strategy => strategyStore._createStrategy(strategy));
} }
function createFeatures () { function createFeatures () {
@ -106,7 +108,7 @@ function createFeatures () {
}, },
}], }],
}, },
], feature => featureDb._createFeature(feature)); ], feature => featureToggleStore._createFeature(feature));
} }
function destroyStrategies () { function destroyStrategies () {