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

fix: use airbnb lint rules directly (#583)

This drops usage of finn-eslint rules as they are no
longer maintained.
This commit is contained in:
Ivar Conradi Østhus 2020-04-14 22:29:11 +02:00 committed by GitHub
parent 42da450a86
commit d01c9d2dac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
116 changed files with 809 additions and 453 deletions

View File

@ -1,13 +1,20 @@
{
"extends": [
"finn",
"finn/node",
"finn-prettier"
],
"parserOptions": {
"ecmaVersion": "2017"
"env": {
"node": true
},
"extends": ["airbnb-base", "prettier"],
"parserOptions": {
"ecmaVersion": "2018"
},
"plugins": ["prettier"],
"root": true,
"rules": {
"class-methods-use-this": [0],
"prettier/prettier": ["error"],
"func-names": "off",
"strict": [0, "global"],
"no-underscore-dangle": "off",
"no-plusplus": "off",
"no-param-reassign": "error",
"no-return-await": "error",
"max-nested-callbacks": "off",

View File

@ -1,12 +1,12 @@
#!/usr/bin/env node
'use strict';
process.env.NODE_ENV = 'production';
const serverImpl = require('../lib/server-impl.js');
const fs = require('fs');
const argv = require('yargs')
const { argv } = require('yargs')
.usage('$0 [options]')
.env(true)
.option('port', {
@ -43,7 +43,9 @@ const argv = require('yargs')
default: 'public',
demand: false,
type: 'string',
}).argv;
});
const serverImpl = require('../lib/server-impl.js');
if (argv.databaseUrlFile) {
argv.databaseUrl = fs.readFileSync(argv.databaseUrlFile, 'utf8');
@ -54,8 +56,10 @@ serverImpl
.start(argv)
.then(instance => {
const address = instance.server.address();
// eslint-disable-next-line no-console
console.log(
`Unleash started on http://${address.address}:${address.port}`
`Unleash started on http://${address.address}:${address.port}`,
);
})
// eslint-disable-next-line no-console
.catch(console.err);

View File

@ -1,3 +1,5 @@
/* eslint-disable import/no-unresolved */
'use strict';
const auth = require('basic-auth');
@ -11,13 +13,13 @@ function basicAuthentication(app) {
// you will need to do some verification of credentials here.
const user = new User({ email: `${credentials.name}@domain.com` });
req.user = user;
next();
} else {
return res
.status('401')
.set({ 'WWW-Authenticate': 'Basic realm="example"' })
.end('access denied');
return next();
}
return res
.status('401')
.set({ 'WWW-Authenticate': 'Basic realm="example"' })
.end('access denied');
});
app.use((req, res, next) => {

View File

@ -12,7 +12,8 @@ unleash
preRouterHook: basicAuth,
})
.then(server => {
// eslint-disable-next-line no-console
console.log(
`Unleash started on http://localhost:${server.app.get('port')}`
`Unleash started on http://localhost:${server.app.get('port')}`,
);
});

View File

@ -21,7 +21,8 @@ unleash
},
})
.then(server => {
// eslint-disable-next-line no-console
console.log(
`Unleash started on http://localhost:${server.app.get('port')}`
`Unleash started on http://localhost:${server.app.get('port')}`,
);
});

View File

@ -1,3 +1,5 @@
/* eslint-disable import/no-extraneous-dependencies */
'use strict';
/**
@ -17,13 +19,13 @@
* - GOOGLE_CALLBACK_URL
*/
// const { User, AuthenticationRequired } = require('unleash-server');
const { User, AuthenticationRequired } = require('../lib/server-impl.js');
const passport = require('@passport-next/passport');
const GoogleOAuth2Strategy = require('@passport-next/passport-google-oauth2')
.Strategy;
// const { User, AuthenticationRequired } = require('unleash-server');
const { User, AuthenticationRequired } = require('../lib/server-impl.js');
passport.use(
new GoogleOAuth2Strategy(
{
@ -38,10 +40,10 @@ passport.use(
new User({
name: profile.displayName,
email: profile.emails[0].value,
})
}),
);
}
)
},
),
);
function enableGoogleOauth(app) {
@ -52,7 +54,7 @@ function enableGoogleOauth(app) {
passport.deserializeUser((user, done) => done(null, user));
app.get(
'/api/admin/login',
passport.authenticate('google', { scope: ['email'] })
passport.authenticate('google', { scope: ['email'] }),
);
app.get(
@ -63,26 +65,25 @@ function enableGoogleOauth(app) {
(req, res) => {
// Successful authentication, redirect to your app.
res.redirect('/');
}
},
);
app.use('/api/admin/', (req, res, next) => {
if (req.user) {
next();
} else {
// Instruct unleash-frontend to pop-up auth dialog
return res
.status('401')
.json(
new AuthenticationRequired({
path: '/api/admin/login',
type: 'custom',
message: `You have to identify yourself in order to use Unleash.
Click the button and follow the instructions.`,
})
)
.end();
return next();
}
// Instruct unleash-frontend to pop-up auth dialog
return res
.status('401')
.json(
new AuthenticationRequired({
path: '/api/admin/login',
type: 'custom',
message: `You have to identify yourself in order to use Unleash.
Click the button and follow the instructions.`,
}),
)
.end();
});
}

View File

@ -12,7 +12,8 @@ unleash
preRouterHook: enableGoogleOauth,
})
.then(server => {
// eslint-disable-next-line no-console
console.log(
`Unleash started on http://localhost:${server.app.get('port')}`
`Unleash started on http://localhost:${server.app.get('port')}`,
);
});

View File

@ -1,3 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable import/no-unresolved */
'use strict';
/**
@ -14,12 +17,12 @@
* - AUTH_CLIENT_ID
*/
// const { User, AuthenticationRequired } = require('unleash-server');
const { User, AuthenticationRequired } = require('../lib/server-impl.js');
const KeycloakStrategy = require('@exlinc/keycloak-passport');
const passport = require('passport');
// const { User, AuthenticationRequired } = require('unleash-server');
const { User, AuthenticationRequired } = require('../lib/server-impl.js');
const host = process.env.AUTH_HOST;
const realm = process.env.AUTH_REALM;
const clientID = process.env.AUTH_CLIENT_ID;
@ -45,10 +48,10 @@ passport.use(
new User({
name: profile.fullName,
email: profile.email,
})
}),
);
}
)
},
),
);
function enableKeycloakOauth(app) {
@ -65,26 +68,25 @@ function enableKeycloakOauth(app) {
passport.authenticate('keycloak'),
(req, res) => {
res.redirect(`${contextPath}/`);
}
},
);
app.use('/api/admin/', (req, res, next) => {
if (req.user) {
next();
} else {
// Instruct unleash-frontend to pop-up auth dialog
return res
.status('401')
.json(
new AuthenticationRequired({
path: `${contextPath}/api/admin/login`,
type: 'custom',
message: `You have to identify yourself in order to use Unleash.
Click the button and follow the instructions.`,
})
)
.end();
return next();
}
// Instruct unleash-frontend to pop-up auth dialog
return res
.status('401')
.json(
new AuthenticationRequired({
path: `${contextPath}/api/admin/login`,
type: 'custom',
message: `You have to identify yourself in order to use Unleash.
Click the button and follow the instructions.`,
}),
)
.end();
});
}

View File

@ -12,7 +12,8 @@ unleash
preRouterHook: enableKeycloak,
})
.then(server => {
// eslint-disable-next-line no-console
console.log(
`Unleash started on http://localhost:${server.app.get('port')}`
`Unleash started on http://localhost:${server.app.get('port')}`,
);
});

View File

@ -4,9 +4,9 @@ const express = require('express');
const compression = require('compression');
const favicon = require('serve-favicon');
const cookieParser = require('cookie-parser');
const IndexRouter = require('./routes');
const path = require('path');
const errorHandler = require('errorhandler');
const IndexRouter = require('./routes');
const unleashSession = require('./middleware/session');
const responseTime = require('./middleware/response-time');
const requestLogger = require('./middleware/request-logger');

View File

@ -4,6 +4,7 @@ const test = require('ava');
const express = require('express');
const proxyquire = require('proxyquire');
const getLogger = require('../test/fixtures/no-logger');
const getApp = proxyquire('./app', {
'./routes': class Index {
router() {

View File

@ -1,11 +1,11 @@
'use strict';
const test = require('ava');
const UnleashClientMetrics = require('./index');
const moment = require('moment');
const lolex = require('lolex');
const { EventEmitter } = require('events');
const UnleashClientMetrics = require('./index');
const appName = 'appName';
const instanceId = 'instanceId';

View File

@ -1,3 +1,5 @@
/* eslint-disable no-param-reassign */
'use strict';
const Projection = require('./projection.js');
@ -25,7 +27,7 @@ module.exports = class UnleashClientMetrics {
Object.keys(toggles).forEach(toggleName => {
this.lastHourProjection.substract(
toggleName,
this.createCountObject(toggles[toggleName])
this.createCountObject(toggles[toggleName]),
);
});
});
@ -33,7 +35,7 @@ module.exports = class UnleashClientMetrics {
Object.keys(toggles).forEach(toggleName => {
this.lastMinuteProjection.substract(
toggleName,
this.createCountObject(toggles[toggleName])
this.createCountObject(toggles[toggleName]),
);
});
});
@ -49,6 +51,7 @@ module.exports = class UnleashClientMetrics {
});
return apps;
}
getSeenTogglesByAppName(appName) {
return this.apps[appName]
? Object.keys(this.apps[appName].seenToggles)
@ -64,7 +67,7 @@ module.exports = class UnleashClientMetrics {
toggles[seenToggleName] = [];
}
toggles[seenToggleName].push({ appName });
}
},
);
});
return toggles;
@ -92,8 +95,8 @@ module.exports = class UnleashClientMetrics {
}
createCountObject(entry) {
let yes = typeof entry.yes == 'number' ? entry.yes : 0;
let no = typeof entry.no == 'number' ? entry.no : 0;
let yes = typeof entry.yes === 'number' ? entry.yes : 0;
let no = typeof entry.no === 'number' ? entry.no : 0;
if (entry.variants) {
Object.entries(entry.variants).forEach(([key, value]) => {

View File

@ -1,3 +1,6 @@
/* eslint-disable no-param-reassign */
/* eslint-disable max-classes-per-file */
'use strict';
const { EventEmitter } = require('events');

View File

@ -1,8 +1,8 @@
'use strict';
const { EventEmitter } = require('events');
const List = require('./list');
const moment = require('moment');
const List = require('./list');
// this list must have entries with sorted ttl range
module.exports = class TTLList extends EventEmitter {
@ -47,7 +47,7 @@ module.exports = class TTLList extends EventEmitter {
timedCheck() {
const now = moment();
this.list.reverseRemoveUntilTrue(({ value }) =>
now.isBefore(value.ttl)
now.isBefore(value.ttl),
);
this.startTimer();
}

View File

@ -1,9 +1,9 @@
'use strict';
const test = require('ava');
const TTLList = require('./ttl-list');
const moment = require('moment');
const lolex = require('lolex');
const TTLList = require('./ttl-list');
test.cb('should emit expire', t => {
const clock = lolex.install();

View File

@ -1,4 +1,5 @@
/* eslint camelcase:off */
'use strict';
const metricsHelper = require('../metrics-helper');
@ -44,6 +45,7 @@ class ClientApplicationsDb {
}
updateRow(details, prev) {
// eslint-disable-next-line no-param-reassign
details.updatedAt = 'now()';
return this.db(TABLE)
.where('app_name', details.appName)
@ -52,7 +54,7 @@ class ClientApplicationsDb {
metricsHelper.wrapTimer(this.eventBus, DB_TIME, {
store: 'applications',
action: 'updateRow',
})
}),
);
}
@ -70,15 +72,14 @@ class ClientApplicationsDb {
.then(result => {
if (result && result[0]) {
return this.updateRow(data, result[0]);
} else {
return this.insertNewRow(data);
}
return this.insertNewRow(data);
})
.then(
metricsHelper.wrapTimer(this.eventBus, DB_TIME, {
store: 'applications',
action: 'upsert',
})
}),
);
}
@ -92,7 +93,7 @@ class ClientApplicationsDb {
metricsHelper.wrapTimer(this.eventBus, DB_TIME, {
store: 'applications',
action: 'getAll',
})
}),
);
}
@ -120,7 +121,7 @@ class ClientApplicationsDb {
.from(TABLE)
.map(mapRow)
.then(apps =>
apps.filter(app => app.strategies.includes(strategyName))
apps.filter(app => app.strategies.includes(strategyName)),
);
}

View File

@ -1,4 +1,5 @@
/* eslint camelcase: "off" */
'use strict';
const metricsHelper = require('../metrics-helper');
@ -40,7 +41,7 @@ class ClientInstanceStore {
.whereRaw("created_at < now() - interval '2 days'")
.del()
.then(
res => res > 0 && this.logger.info(`Deleted ${res} instances`)
res => res > 0 && this.logger.info(`Deleted ${res} instances`),
);
}
@ -73,15 +74,14 @@ class ClientInstanceStore {
.then(rows => {
if (rows[0].count > 0) {
return this.updateRow(details);
} else {
return this.insertNewRow(details);
}
return this.insertNewRow(details);
})
.then(
metricsHelper.wrapTimer(this.eventBus, DB_TIME, {
store: 'instance',
action: 'insert',
})
}),
);
}
@ -95,7 +95,7 @@ class ClientInstanceStore {
metricsHelper.wrapTimer(this.eventBus, DB_TIME, {
store: 'instance',
action: 'getAll',
})
}),
);
}

View File

@ -52,7 +52,7 @@ class ClientMetricsStore extends EventEmitter {
metricsHelper.wrapTimer(this.eventBus, DB_TIME, {
store: 'metrics',
action: 'insert',
})
}),
);
}

View File

@ -2,8 +2,8 @@
const test = require('ava');
const { EventEmitter } = require('events');
const ClientMetricStore = require('./client-metrics-store');
const lolex = require('lolex');
const ClientMetricStore = require('./client-metrics-store');
const getLogger = require('../../test/fixtures/no-logger');
function getMockDb() {
@ -42,7 +42,7 @@ test.cb('should call database on startup', t => {
test.cb('should start poller even if inital database fetch fails', t => {
const clock = lolex.install();
const mock = getMockDb();
mock.getMetricsLastHour = () => Promise.reject('oops');
mock.getMetricsLastHour = () => Promise.reject(new Error('oops'));
const ee = new EventEmitter();
const store = new ClientMetricStore(mock, ee, getLogger, 100);

View File

@ -30,10 +30,10 @@ class ContextFieldStore {
this._createFromConfig(customContextFields);
eventStore.on(CONTEXT_FIELD_CREATED, event =>
this._createContextField(event.data)
this._createContextField(event.data),
);
eventStore.on(CONTEXT_FIELD_UPDATED, event =>
this._updateContextField(event.data)
this._updateContextField(event.data),
);
eventStore.on(CONTEXT_FIELD_DELETED, event => {
this._deleteContextField(event.data);
@ -44,7 +44,7 @@ class ContextFieldStore {
if (customContextFields) {
this.logger.info(
'Create custom context fields',
customContextFields
customContextFields,
);
const conextFields = await this.getAll();
customContextFields
@ -86,11 +86,13 @@ class ContextFieldStore {
}
_createContextField(contextField) {
console.log('insert', contextField);
return this.db(TABLE)
.insert(this.fieldToRow(contextField))
.catch(err =>
this.logger.error('Could not insert contextField, error: ', err)
this.logger.error(
'Could not insert contextField, error: ',
err,
),
);
}
@ -101,8 +103,8 @@ class ContextFieldStore {
.catch(err =>
this.logger.error(
'Could not update context field, error: ',
err
)
err,
),
);
}
@ -113,7 +115,7 @@ class ContextFieldStore {
.catch(err => {
this.logger.error(
'Could not delete context field, error: ',
err
err,
);
});
}

View File

@ -1,7 +1,7 @@
'use strict';
const { DROP_FEATURES } = require('../event-type');
const { EventEmitter } = require('events');
const { DROP_FEATURES } = require('../event-type');
const EVENT_COLUMNS = ['id', 'type', 'created_by', 'created_at', 'data'];
@ -42,7 +42,7 @@ class EventStore extends EventEmitter {
this.db
.select(this.db.raw('coalesce(max(id),0) as id'))
.from('events')
.where({ type: DROP_FEATURES })
.where({ type: DROP_FEATURES }),
)
.orderBy('created_at', 'desc')
.map(this.rowToEvent);

View File

@ -9,6 +9,7 @@ const {
DROP_FEATURES,
} = require('../event-type');
const NotFoundError = require('../error/notfound-error');
const FEATURE_COLUMNS = [
'name',
'description',
@ -24,16 +25,16 @@ class FeatureToggleStore {
this.db = db;
this.getLogger = getLogger('client-toggle-store.js');
eventStore.on(FEATURE_CREATED, event =>
this._createFeature(event.data)
this._createFeature(event.data),
);
eventStore.on(FEATURE_UPDATED, event =>
this._updateFeature(event.data)
this._updateFeature(event.data),
);
eventStore.on(FEATURE_ARCHIVED, event =>
this._archiveFeature(event.data)
this._archiveFeature(event.data),
);
eventStore.on(FEATURE_REVIVED, event =>
this._reviveFeature(event.data)
this._reviveFeature(event.data),
);
eventStore.on(FEATURE_IMPORT, event => this._importFeature(event.data));
eventStore.on(DROP_FEATURES, () => this._dropFeatures());
@ -119,7 +120,7 @@ class FeatureToggleStore {
return this.db(TABLE)
.insert(this.eventDataToRow(data))
.catch(err =>
this.logger.error('Could not insert feature, error: ', err)
this.logger.error('Could not insert feature, error: ', err),
);
}
@ -128,7 +129,7 @@ class FeatureToggleStore {
.where({ name: data.name })
.update(this.eventDataToRow(data))
.catch(err =>
this.logger.error('Could not update feature, error: ', err)
this.logger.error('Could not update feature, error: ', err),
);
}
@ -146,7 +147,7 @@ class FeatureToggleStore {
.where({ name })
.update({ archived: 0, enabled: 0 })
.catch(err =>
this.logger.error('Could not archive feature, error: ', err)
this.logger.error('Could not archive feature, error: ', err),
);
}
@ -156,10 +157,10 @@ class FeatureToggleStore {
.where({ name: rowData.name })
.update(rowData)
.then(result =>
result === 0 ? this.db(TABLE).insert(rowData) : result
result === 0 ? this.db(TABLE).insert(rowData) : result,
)
.catch(err =>
this.logger.error('Could not import feature, error: ', err)
this.logger.error('Could not import feature, error: ', err),
);
}
@ -167,7 +168,7 @@ class FeatureToggleStore {
return this.db(TABLE)
.delete()
.catch(err =>
this.logger.error('Could not drop features, error: ', err)
this.logger.error('Could not drop features, error: ', err),
);
}
}

View File

@ -12,7 +12,7 @@ const ContextFieldStore = require('./context-field-store');
const SettingStore = require('./setting-store');
module.exports.createStores = (config, eventBus) => {
const getLogger = config.getLogger;
const { getLogger } = config;
const db = createDb(config);
const eventStore = new EventStore(db, getLogger);
const clientMetricsDb = new ClientMetricsDb(db, getLogger);
@ -25,19 +25,19 @@ module.exports.createStores = (config, eventBus) => {
clientApplicationsStore: new ClientApplicationsStore(
db,
eventBus,
getLogger
getLogger,
),
clientInstanceStore: new ClientInstanceStore(db, eventBus, getLogger),
clientMetricsStore: new ClientMetricsStore(
clientMetricsDb,
eventBus,
getLogger
getLogger,
),
contextFieldStore: new ContextFieldStore(
db,
config.customContextFields,
eventStore,
getLogger
getLogger,
),
settingStore: new SettingStore(db, getLogger),
};

View File

@ -1,4 +1,5 @@
/* eslint camelcase: "off" */
'use strict';
const TABLE = 'settings';
@ -29,9 +30,8 @@ class SettingStore {
.then(rows => {
if (rows[0].count > 0) {
return this.updateRow(name, content);
} else {
return this.insertNewRow(name, content);
}
return this.insertNewRow(name, content);
});
}
@ -43,9 +43,8 @@ class SettingStore {
if (result.length > 0) {
return result[0].content;
} else {
return undefined;
}
return undefined;
}
}

View File

@ -8,6 +8,7 @@ const {
DROP_STRATEGIES,
} = require('../event-type');
const NotFoundError = require('../error/notfound-error');
const STRATEGY_COLUMNS = ['name', 'description', 'parameters', 'built_in'];
const TABLE = 'strategies';
@ -16,16 +17,16 @@ class StrategyStore {
this.db = db;
this.logger = getLogger('strategy-store.js');
eventStore.on(STRATEGY_CREATED, event =>
this._createStrategy(event.data)
this._createStrategy(event.data),
);
eventStore.on(STRATEGY_UPDATED, event =>
this._updateStrategy(event.data)
this._updateStrategy(event.data),
);
eventStore.on(STRATEGY_DELETED, event =>
this._deleteStrategy(event.data)
this._deleteStrategy(event.data),
);
eventStore.on(STRATEGY_IMPORT, event =>
this._importStrategy(event.data)
this._importStrategy(event.data),
);
eventStore.on(DROP_STRATEGIES, () => this._dropStrategies());
}
@ -90,7 +91,7 @@ class StrategyStore {
this.db(TABLE)
.insert(this.eventDataToRow(data))
.catch(err =>
this.logger.error('Could not insert strategy, error: ', err)
this.logger.error('Could not insert strategy, error: ', err),
);
}
@ -99,7 +100,7 @@ class StrategyStore {
.where({ name: data.name })
.update(this.eventDataToRow(data))
.catch(err =>
this.logger.error('Could not update strategy, error: ', err)
this.logger.error('Could not update strategy, error: ', err),
);
}
@ -118,10 +119,10 @@ class StrategyStore {
.where({ name: rowData.name, built_in: 0 }) // eslint-disable-line
.update(rowData)
.then(result =>
result === 0 ? this.db(TABLE).insert(rowData) : result
result === 0 ? this.db(TABLE).insert(rowData) : result,
)
.catch(err =>
this.logger.error('Could not import strategy, error: ', err)
this.logger.error('Could not import strategy, error: ', err),
);
}
@ -130,7 +131,7 @@ class StrategyStore {
.where({ built_in: 0 }) // eslint-disable-line
.delete()
.catch(err =>
this.logger.error('Could not drop strategies, error: ', err)
this.logger.error('Could not drop strategies, error: ', err),
);
}
}

View File

@ -1,5 +1,8 @@
/* eslint-disable no-param-reassign */
'use strict';
const { diff } = require('deep-diff');
const {
STRATEGY_CREATED,
STRATEGY_DELETED,
@ -16,7 +19,6 @@ const {
CONTEXT_FIELD_UPDATED,
CONTEXT_FIELD_DELETED,
} = require('./event-type');
const diff = require('deep-diff').diff;
const strategyTypes = [
STRATEGY_CREATED,
@ -44,9 +46,11 @@ const contextTypes = [
function baseTypeFor(event) {
if (featureTypes.indexOf(event.type) !== -1) {
return 'features';
} else if (strategyTypes.indexOf(event.type) !== -1) {
}
if (strategyTypes.indexOf(event.type) !== -1) {
return 'strategies';
} else if (contextTypes.indexOf(event.type) !== -1) {
}
if (contextTypes.indexOf(event.type) !== -1) {
return 'context';
}
throw new Error(`unknown event type: ${JSON.stringify(event)}`);
@ -91,6 +95,7 @@ function eachConsecutiveEvent(events, callback) {
}
function addDiffs(events) {
// TODO: no-param-reassign
eachConsecutiveEvent(events, (left, right) => {
if (right) {
left.diffs = diff(right.data, left.data);

View File

@ -44,7 +44,7 @@ test('diffs a feature-update event', t => {
eventDiffer.addDiffs(events);
const diffs = events[0].diffs;
const { diffs } = events[0];
t.true(diffs[0].kind === 'E');
t.true(diffs[0].path[0] === 'enabled');
t.true(diffs[0].kind === 'E');

View File

@ -2,6 +2,7 @@
const test = require('ava');
const { EventEmitter } = require('events');
const eventStore = new EventEmitter();
const { addEventHook } = require('./event-hook');
const {
@ -28,5 +29,5 @@ test.before(() => {
eventStore.emit(feature, data);
t.true(o[feature] === data);
});
}
},
);

View File

@ -2,31 +2,6 @@
const log4js = require('log4js');
let loggerProvider = getDefaultLogProvider();
module.exports.defaultLogProvider = loggerProvider;
function validateLogProvider(provider) {
validate(typeof provider == 'function', 'Provider needs to be a function');
const logger = provider('unleash:logger');
validate(typeof logger.debug == 'function', 'Logger must implement debug');
validate(typeof logger.info == 'function', 'Logger must implement info');
validate(typeof logger.warn == 'function', 'Logger must implement warn');
validate(typeof logger.error == 'function', 'Logger must implement error');
}
exports.validateLogProvider = validateLogProvider;
// Deprecated
exports.setLoggerProvider = function setLoggerProvider(provider) {
validateLogProvider(provider);
loggerProvider = provider;
const logger = provider('unleash:logger');
logger.info(`Your way of configuring a logProvider is depreacted.
See https://unleash.github.io/docs/getting_started for details`);
};
function getDefaultLogProvider() {
let level;
if (process.env.NODE_ENV === 'production') {
@ -49,8 +24,34 @@ function getDefaultLogProvider() {
return log4js.getLogger;
}
let loggerProvider = getDefaultLogProvider();
function validate(isValid, msg) {
if (!isValid) {
throw new TypeError(msg);
}
}
module.exports.defaultLogProvider = loggerProvider;
function validateLogProvider(provider) {
validate(typeof provider === 'function', 'Provider needs to be a function');
const logger = provider('unleash:logger');
validate(typeof logger.debug === 'function', 'Logger must implement debug');
validate(typeof logger.info === 'function', 'Logger must implement info');
validate(typeof logger.warn === 'function', 'Logger must implement warn');
validate(typeof logger.error === 'function', 'Logger must implement error');
}
exports.validateLogProvider = validateLogProvider;
// Deprecated
exports.setLoggerProvider = function setLoggerProvider(provider) {
validateLogProvider(provider);
loggerProvider = provider;
const logger = provider('unleash:logger');
logger.info(`Your way of configuring a logProvider is depreacted.
See https://unleash.github.io/docs/getting_started for details`);
};

View File

@ -18,7 +18,7 @@ test('should require custom logger to implement info', t => {
() => {
logger.setLoggerProvider(provider)();
},
{ instanceOf: TypeError }
{ instanceOf: TypeError },
);
t.is(error.message, 'Logger must implement info');
});
@ -34,7 +34,7 @@ test('should require custom logger to implement error', t => {
() => {
logger.setLoggerProvider(provider)();
},
{ instanceOf: TypeError }
{ instanceOf: TypeError },
);
t.is(error.message, 'Logger must implement error');
});

View File

@ -1,3 +1,5 @@
/* eslint-disable no-param-reassign */
'use strict';
const timer = require('./timer');

View File

@ -1,5 +1,6 @@
'use strict';
const client = require('prom-client');
const events = require('./events');
const {
FEATURE_CREATED,
@ -8,6 +9,7 @@ const {
FEATURE_REVIVED,
} = require('./event-type');
const { version } = require('./routes/api-def');
const THREE_HOURS = 3 * 60 * 60 * 1000;
exports.startMonitoring = (
@ -15,14 +17,12 @@ exports.startMonitoring = (
eventBus,
eventStore,
clientMetricsStore,
featureToggleStore
featureToggleStore,
) => {
if (!enable) {
return;
}
const client = require('prom-client');
client.collectDefaultMetrics();
const requestDuration = new client.Summary({
@ -84,6 +84,7 @@ exports.startMonitoring = (
});
clientMetricsStore.on('metrics', m => {
// eslint-disable-next-line no-restricted-syntax
for (const [feature, { yes, no }] of Object.entries(m.bucket.toggles)) {
featureToggleUsageTotal.labels(feature, true, m.appName).inc(yes);
featureToggleUsageTotal.labels(feature, false, m.appName).inc(no);

View File

@ -2,13 +2,14 @@
const test = require('ava');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
const eventStore = new EventEmitter();
const clientMetricsStore = new EventEmitter();
const { register: prometheusRegister } = require('prom-client');
const { REQUEST_TIME, DB_TIME } = require('./events');
const { FEATURE_UPDATED } = require('./event-type');
const { startMonitoring } = require('./metrics');
const { register: prometheusRegister } = require('prom-client');
test.before(() => {
const featureToggleStore = {
@ -19,7 +20,7 @@ test.before(() => {
eventBus,
eventStore,
clientMetricsStore,
featureToggleStore
featureToggleStore,
);
});
@ -34,7 +35,7 @@ test('should collect metrics for requests', t => {
const metrics = prometheusRegister.metrics();
t.regex(
metrics,
/http_request_duration_milliseconds{quantile="0\.99",path="somePath",method="GET",status="200"} 1337/
/http_request_duration_milliseconds{quantile="0\.99",path="somePath",method="GET",status="200"} 1337/,
);
});
@ -62,7 +63,7 @@ test('should collect metrics for client metric reports', t => {
const metrics = prometheusRegister.metrics();
t.regex(
metrics,
/feature_toggle_usage_total{toggle="TestToggle",active="true",appName="undefined"} 10\nfeature_toggle_usage_total{toggle="TestToggle",active="false",appName="undefined"} 5/
/feature_toggle_usage_total{toggle="TestToggle",active="true",appName="undefined"} 10\nfeature_toggle_usage_total{toggle="TestToggle",active="false",appName="undefined"} 5/,
);
});
@ -76,7 +77,7 @@ test('should collect metrics for db query timings', t => {
const metrics = prometheusRegister.metrics();
t.regex(
metrics,
/db_query_duration_seconds{quantile="0\.99",store="foo",action="bar"} 0.1337/
/db_query_duration_seconds{quantile="0\.99",store="foo",action="bar"} 0.1337/,
);
});

View File

@ -11,7 +11,7 @@ test('should add dummy user object to all requests', t => {
const app = express();
noAuthentication('', app);
app.get('/api/admin/test', (req, res) => {
const user = Object.assign({}, req.user);
const user = { ...req.user };
return res
.status(200)

View File

@ -22,7 +22,7 @@ module.exports = function(config, permission) {
new MissingPermission({
permission,
message: `You require ${permission} to perform this action`,
})
}),
)
.end();
};

View File

@ -1,13 +1,13 @@
'use strict';
const test = require('ava');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../test/fixtures/store');
const checkPermission = require('./permission-checker');
const supertest = require('supertest');
const getApp = require('../app');
const getLogger = require('../../test/fixtures/no-logger');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup(preRouterHook) {
@ -28,7 +28,7 @@ function getSetup(preRouterHook) {
res.status(200)
.json({ message: 'OK' })
.end();
}
},
);
},
});

View File

@ -21,20 +21,19 @@ function unsecureAuthentication(basePath = '', app) {
app.use(`${basePath}/api/admin/`, (req, res, next) => {
if (req.user) {
next();
} else {
return res
.status('401')
.json(
new AuthenticationRequired({
path: `${basePath}/api/admin/login`,
type: 'unsecure',
message:
'You have to indentify yourself in order to use Unleash.',
})
)
.end();
return next();
}
return res
.status('401')
.json(
new AuthenticationRequired({
path: `${basePath}/api/admin/login`,
type: 'unsecure',
message:
'You have to indentify yourself in order to use Unleash.',
}),
)
.end();
});
app.use((req, res, next) => {

View File

@ -8,6 +8,16 @@ const { defaultLogProvider, validateLogProvider } = require('./logger');
const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
function defaultDatabaseUrl() {
if (process.env.DATABASE_URL_FILE) {
return readFileSync(process.env.DATABASE_URL_FILE, 'utf8');
}
if (process.env.DATABASE_URL) {
return process.env.DATABASE_URL;
}
return undefined;
}
function defaultOptions() {
return {
databaseUrl: defaultDatabaseUrl(),
@ -41,16 +51,6 @@ function defaultOptions() {
};
}
function defaultDatabaseUrl() {
if (process.env.DATABASE_URL_FILE) {
return readFileSync(process.env.DATABASE_URL_FILE, 'utf8');
} else if (process.env.DATABASE_URL) {
return process.env.DATABASE_URL;
} else {
return undefined;
}
}
module.exports = {
createOptions: (opts = {}) => {
const options = merge(defaultOptions(), opts);
@ -62,7 +62,7 @@ module.exports = {
if (!options.db.host) {
throw new Error(
`Unleash requires database details to start. See https://unleash.github.io/docs/getting_started`
`Unleash requires database details to start. See https://unleash.github.io/docs/getting_started`,
);
}

View File

@ -1,13 +1,14 @@
/* eslint-disable no-console */
'use strict';
const test = require('ava');
const fs = require('fs');
const { createOptions } = require('./options');
delete process.env.DATABASE_URL;
test('should require DATABASE_URI', t => {
const { createOptions } = require('./options');
t.throws(() => {
createOptions({});
});
@ -17,7 +18,6 @@ test('should use DATABASE_URL from env', t => {
const databaseUrl = 'postgres://u:p@localhost:5432/name';
delete process.env.NODE_ENV;
process.env.DATABASE_URL = databaseUrl;
const { createOptions } = require('./options');
const options = createOptions({});
@ -30,7 +30,6 @@ test('should use DATABASE_URL_FILE from env', t => {
fs.writeFileSync(path, databaseUrl, { mode: 0o755 });
delete process.env.NODE_ENV;
process.env.DATABASE_URL_FILE = path;
const { createOptions } = require('./options');
const options = createOptions({});
@ -39,7 +38,6 @@ test('should use DATABASE_URL_FILE from env', t => {
test('should use databaseUrl from options', t => {
const databaseUrl = 'postgres://u:p@localhost:5432/name';
const { createOptions } = require('./options');
const options = createOptions({ databaseUrl });
@ -50,7 +48,6 @@ test('should not override provided options', t => {
process.env.DATABASE_URL = 'postgres://test:5432/name';
process.env.NODE_ENV = 'production';
const { createOptions } = require('./options');
const options = createOptions({
databaseUrl: 'postgres://test:5432/name',
port: 1111,
@ -61,7 +58,6 @@ test('should not override provided options', t => {
});
test('should add listen options from host and port', t => {
const { createOptions } = require('./options');
const options = createOptions({
databaseUrl: 'postgres://test:5432/name',
port: 1111,
@ -72,7 +68,6 @@ test('should add listen options from host and port', t => {
});
test('should use pipe to path', t => {
const { createOptions } = require('./options');
const options = createOptions({
databaseUrl: 'postgres://test:5432/name',
port: 1111,
@ -86,7 +81,6 @@ test('should use pipe to path', t => {
test('should prefer databaseUrl from options', t => {
process.env.DATABASE_URL = 'postgres://test:5432/name';
const databaseUrl = 'postgres://u:p@localhost:5432/options';
const { createOptions } = require('./options');
const options = createOptions({ databaseUrl });
@ -96,7 +90,6 @@ test('should prefer databaseUrl from options', t => {
test('should expand databaseUrl from options', t => {
process.env.DATABASE_URL = 'postgres://test:5432/name';
const databaseUrl = 'postgres://u:p@localhost:5432/options';
const { createOptions } = require('./options');
const options = createOptions({ databaseUrl });
@ -113,7 +106,6 @@ test('should expand databaseUrl from options', t => {
test('should validate getLogger', t => {
const databaseUrl = 'postgres://u:p@localhost:5432/options';
const getLogger = () => {};
const { createOptions } = require('./options');
t.throws(() => {
createOptions({ databaseUrl, getLogger });
@ -128,7 +120,6 @@ test('should accept custome log-provider', t => {
warn: console.log,
error: console.log,
});
const { createOptions } = require('./options');
const options = createOptions({ databaseUrl, getLogger });
t.deepEqual(options.getLogger, getLogger);
@ -145,7 +136,6 @@ test('should prefer custom db connection options', t => {
ssl: false,
driver: 'postgres',
};
const { createOptions } = require('./options');
const options = createOptions({ databaseUrl, db });
t.deepEqual(options.db, db);
@ -154,7 +144,6 @@ test('should prefer custom db connection options', t => {
test('should baseUriPath', t => {
const databaseUrl = 'postgres://u:p@localhost:5432/options';
const baseUriPath = 'some';
const { createOptions } = require('./options');
const options = createOptions({ databaseUrl, baseUriPath });
t.deepEqual(options.baseUriPath, baseUriPath);

View File

@ -31,7 +31,7 @@ class ArchiveController extends Controller {
createdBy: userName,
data: { name: req.params.name },
});
res.status(200).end();
return res.status(200).end();
} catch (error) {
this.logger.error('Server failed executing request', error);
return res.status(500).end();

View File

@ -1,14 +1,14 @@
'use strict';
const test = require('ava');
const store = require('./../../../test/fixtures/store');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const permissions = require('../../../test/fixtures/permissions');
const getLogger = require('../../../test/fixtures/no-logger');
const supertest = require('supertest');
const getApp = require('../../app');
const { UPDATE_FEATURE } = require('../../permissions');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {

View File

@ -1,12 +1,12 @@
'use strict';
const test = require('ava');
const store = require('./../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const getApp = require('../../app');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
const uiConfig = {

View File

@ -32,12 +32,12 @@ class ContextController extends Controller {
this.put(
'/:contextField',
this.updateContextField,
UPDATE_CONTEXT_FIELD
UPDATE_CONTEXT_FIELD,
);
this.delete(
'/:contextField',
this.deleteContextField,
DELETE_CONTEXT_FIELD
DELETE_CONTEXT_FIELD,
);
this.post('/validate', this.validate);
}
@ -60,7 +60,7 @@ class ContextController extends Controller {
}
async createContextField(req, res) {
const name = req.body.name;
const { name } = req.body;
const userName = extractUser(req);
try {
@ -130,7 +130,7 @@ class ContextController extends Controller {
}
async validate(req, res) {
const name = req.body.name;
const { name } = req.body;
try {
await nameSchema.validateAsync({ name });

View File

@ -1,12 +1,12 @@
'use strict';
const test = require('ava');
const store = require('./../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const getApp = require('../../app');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {

View File

@ -3,6 +3,7 @@
const Controller = require('../controller');
const eventDiffer = require('../../event-differ');
const version = 1;
class EventController extends Controller {

View File

@ -2,13 +2,13 @@
const test = require('ava');
const store = require('./../../../test/fixtures/store');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const supertest = require('supertest');
const getApp = require('../../app');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {

View File

@ -41,7 +41,7 @@ const variantsSchema = joi.object().keys({
contextName: joi.string().required(),
values: joi.array().items(joi.string()),
})
.optional()
.optional(),
),
});

View File

@ -91,7 +91,7 @@ test('variant overrides must have corect shape', async t => {
} catch (error) {
t.is(
error.details[0].message,
'"variants[0].overrides" must be an array'
'"variants[0].overrides" must be an array',
);
}
});

View File

@ -16,6 +16,7 @@ const {
CREATE_FEATURE,
} = require('../../permissions');
const { featureShema, nameSchema } = require('./feature-schema');
const version = 1;
class FeatureController extends Controller {
@ -52,7 +53,7 @@ class FeatureController extends Controller {
}
async validate(req, res) {
const name = req.body.name;
const { name } = req.body;
try {
await nameSchema.validateAsync({ name });
@ -99,7 +100,7 @@ class FeatureController extends Controller {
}
async updateToggle(req, res) {
const featureName = req.params.featureName;
const { featureName } = req.params;
const userName = extractUser(req);
const updatedFeature = req.body;
@ -140,12 +141,12 @@ class FeatureController extends Controller {
}
async _toggle(enabled, req, res) {
const featureName = req.params.featureName;
const { featureName } = req.params;
const userName = extractUser(req);
try {
const feature = await this.featureToggleStore.getFeature(
featureName
featureName,
);
feature.enabled = enabled;
@ -161,7 +162,7 @@ class FeatureController extends Controller {
}
async deleteToggle(req, res) {
const featureName = req.params.featureName;
const { featureName } = req.params;
const userName = extractUser(req);
try {

View File

@ -1,14 +1,14 @@
'use strict';
const test = require('ava');
const store = require('./../../../test/fixtures/store');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const permissions = require('../../../test/fixtures/permissions');
const getLogger = require('../../../test/fixtures/no-logger');
const supertest = require('supertest');
const getApp = require('../../app');
const { UPDATE_FEATURE, CREATE_FEATURE } = require('../../permissions');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {
@ -135,7 +135,7 @@ test('should not be allowed to reuse active toggle name', t => {
.expect(res => {
t.true(
res.body.details[0].message ===
'A toggle with that name already exist'
'A toggle with that name already exist',
);
});
});
@ -156,7 +156,7 @@ test('should not be allowed to reuse archived toggle name', t => {
.expect(res => {
t.true(
res.body.details[0].message ===
'An archived toggle with that name already exist'
'An archived toggle with that name already exist',
);
});
});
@ -201,8 +201,8 @@ test('valid feature names should pass validation', t => {
strategies: [{ name: 'default' }],
})
.set('Content-Type', 'application/json')
.expect(201)
)
.expect(201),
),
);
});
@ -229,8 +229,8 @@ test('invalid feature names should not pass validation', t => {
strategies: [{ name: 'default' }],
})
.set('Content-Type', 'application/json')
.expect(400)
)
.expect(400),
),
);
});
@ -253,7 +253,7 @@ test('invalid feature names should have error msg', t => {
.expect(400)
.expect(res => {
t.true(
res.body.details[0].message === '"name" must be URL friendly'
res.body.details[0].message === '"name" must be URL friendly',
);
});
});

View File

@ -30,7 +30,7 @@ class MetricsController extends Controller {
this.post(
'/applications/:appName',
this.createApplication,
UPDATE_APPLICATION
UPDATE_APPLICATION,
);
this.get('/applications/', this.getApplications);
this.get('/applications/:appName', this.getApplication);
@ -45,6 +45,7 @@ class MetricsController extends Controller {
const seenApps = this.metrics.getSeenAppsPerToggle();
const applications = await this.clientApplicationsStore.getApplications();
const metaData = applications.reduce((result, entry) => {
// eslint-disable-next-line no-param-reassign
result[entry.appName] = entry;
return result;
}, {});
@ -52,7 +53,7 @@ class MetricsController extends Controller {
Object.keys(seenApps).forEach(key => {
seenApps[key] = seenApps[key].map(entry => {
if (metaData[entry.appName]) {
return Object.assign({}, entry, metaData[entry.appName]);
return { ...entry, ...metaData[entry.appName] };
}
return entry;
});
@ -65,7 +66,7 @@ class MetricsController extends Controller {
}
getFeatureToggle(req, res) {
const name = req.params.name;
const { name } = req.params;
const data = this.metrics.getTogglesMetrics();
const lastHour = data.lastHour[name] || {};
const lastMinute = data.lastMinute[name] || {};
@ -76,9 +77,7 @@ class MetricsController extends Controller {
}
async createApplication(req, res) {
const input = Object.assign({}, req.body, {
appName: req.params.appName,
});
const input = { ...req.body, appName: req.params.appName };
const { value: applicationData, error } = schema.validate(input);
if (error) {
@ -88,17 +87,17 @@ class MetricsController extends Controller {
try {
await this.clientApplicationsStore.upsert(applicationData);
res.status(202).end();
return res.status(202).end();
} catch (err) {
this.logger.error(err);
res.status(500).end();
return res.status(500).end();
}
}
async getApplications(req, res) {
try {
const applications = await this.clientApplicationsStore.getApplications(
req.query
req.query,
);
res.json({ applications });
} catch (err) {
@ -108,7 +107,7 @@ class MetricsController extends Controller {
}
async getApplication(req, res) {
const appName = req.params.appName;
const { appName } = req.params;
const seenToggles = this.metrics.getSeenTogglesByAppName(appName);
try {
@ -133,12 +132,12 @@ class MetricsController extends Controller {
icon: application.icon,
strategies: application.strategies.map(name => {
const found = strategies.find(f => f.name === name);
return found ? found : { name, notFound: true };
return found || { name, notFound: true };
}),
instances,
seenToggles: seenToggles.map(name => {
const found = features.find(f => f.name === name);
return found ? found : { name, notFound: true };
return found || { name, notFound: true };
}),
links: {
self: `/api/applications/${application.appName}`,

View File

@ -1,14 +1,14 @@
'use strict';
const test = require('ava');
const store = require('./../../../test/fixtures/store');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const permissions = require('../../../test/fixtures/permissions');
const getLogger = require('../../../test/fixtures/no-logger');
const supertest = require('supertest');
const getApp = require('../../app');
const { UPDATE_APPLICATION } = require('../../permissions');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {

View File

@ -1,13 +1,14 @@
'use strict';
const Controller = require('../controller');
const { ADMIN } = require('../../permissions');
const extractUser = require('../../extract-user');
const { handleErrors } = require('./util');
const mime = require('mime');
const YAML = require('js-yaml');
const moment = require('moment');
const multer = require('multer');
const Controller = require('../controller');
const { ADMIN } = require('../../permissions');
const extractUser = require('../../extract-user');
const { handleErrors } = require('./util');
const upload = multer({ limits: { fileSize: 5242880 } });
class StateController extends Controller {

View File

@ -24,7 +24,7 @@ const strategySchema = joi.object().keys({
.allow('')
.optional(),
required: joi.boolean(),
})
}),
),
});

View File

@ -12,6 +12,7 @@ const {
CREATE_STRATEGY,
UPDATE_STRATEGY,
} = require('../../permissions');
const version = 1;
class StrategyController extends Controller {
@ -35,7 +36,7 @@ class StrategyController extends Controller {
async getStrategy(req, res) {
try {
const name = req.params.name;
const { name } = req.params;
const strategy = await this.strategyStore.getStrategy(name);
res.json(strategy).end();
} catch (err) {
@ -104,9 +105,9 @@ class StrategyController extends Controller {
.then(() =>
reject(
new NameExistsError(
`Strategy with name ${data.name} already exist!`
)
)
`Strategy with name ${data.name} already exist!`,
),
),
)
.catch(() => resolve(data));
});

View File

@ -1,10 +1,11 @@
'use strict';
const test = require('ava');
const store = require('./../../../test/fixtures/store');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const permissions = require('../../../test/fixtures/permissions');
const getLogger = require('../../../test/fixtures/no-logger');
const supertest = require('supertest');
const getApp = require('../../app');
const {
DELETE_STRATEGY,
@ -12,7 +13,6 @@ const {
UPDATE_STRATEGY,
} = require('../../permissions');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {
@ -75,7 +75,7 @@ test('require parameters array when creating a new stratey', t => {
.expect(res => {
t.deepEqual(
res.body.details[0].message,
'"parameters" is required'
'"parameters" is required',
);
});
});

View File

@ -11,7 +11,7 @@ class UserController extends Controller {
getUser(req, res) {
if (req.user) {
const user = Object.assign({}, req.user);
const user = { ...req.user };
if (!this.config.extendedPermissions) {
delete user.permissions;
} else if (!Array.isArray(user.permissions)) {
@ -21,9 +21,8 @@ class UserController extends Controller {
.status(200)
.json(user)
.end();
} else {
return res.status(404).end();
}
return res.status(404).end();
}
// Depcreated, use "/logout" instead. Will be removed in later release.

View File

@ -1,13 +1,13 @@
'use strict';
const test = require('ava');
const store = require('./../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const getApp = require('../../app');
const User = require('../../user');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
const currentUser = new User({ email: 'test@mail.com' });

View File

@ -14,6 +14,7 @@ const customJoi = joi.extend(j => ({
// Generate an error, state and options need to be passed
return { value, errors: helpers.error('isUrlFriendly.base') };
}
return undefined;
},
}));
@ -30,6 +31,7 @@ const handleErrors = (res, logger, error) => {
return res.status(404).end();
case 'NameExistsError':
case 'ValidationError':
// eslint-disable-next-line no-param-reassign
error.isJoi = true;
return res
.status(400)

View File

@ -2,7 +2,8 @@
// export module version
require('pkginfo')(module, 'version');
const version = module.exports.version;
const { version } = module.exports;
const clientApiDef = require('./client-api/api-def.json');
const adminApiDef = require('./admin-api/api-def.json');

View File

@ -1,12 +1,12 @@
'use strict';
const test = require('ava');
const store = require('./../../test/fixtures/store');
const getLogger = require('../../test/fixtures/no-logger');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../test/fixtures/store');
const getLogger = require('../../test/fixtures/no-logger');
const getApp = require('../app');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
test('should use enable prometheus', t => {

View File

@ -1,12 +1,12 @@
'use strict';
const test = require('ava');
const store = require('./../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const getApp = require('../../app');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {

View File

@ -10,8 +10,8 @@ class ClientApi extends Controller {
constructor(config) {
super();
const stores = config.stores;
const getLogger = config.getLogger;
const { stores } = config;
const { getLogger } = config;
this.get('/', this.index);
this.use('/features', new FeatureController(stores, getLogger).router);

View File

@ -31,10 +31,10 @@ class ClientMetricsController extends Controller {
instanceId: value.instanceId,
clientIp,
});
res.status(202).end();
return res.status(202).end();
} catch (e) {
this.logger.error('failed to store metrics', e);
res.status(500).end();
return res.status(500).end();
}
}
}

View File

@ -2,12 +2,12 @@
const test = require('ava');
const supertest = require('supertest');
const store = require('./../../../test/fixtures/store');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const getApp = require('../../app');
const { clientMetricsSchema } = require('./metrics-schema');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {

View File

@ -29,7 +29,7 @@ class RegisterController extends Controller {
await this.clientInstanceStore.insert(value);
const { appName, instanceId } = value;
this.logger.info(
`New client registration: appName=${appName}, instanceId=${instanceId}`
`New client registration: appName=${appName}, instanceId=${instanceId}`,
);
return res.status(202).end();
} catch (err) {

View File

@ -1,12 +1,12 @@
'use strict';
const test = require('ava');
const store = require('./../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const getApp = require('../../app');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {

View File

@ -16,7 +16,7 @@ class Controller {
this.app.get(
path,
checkPermission(this.config, permission),
handler.bind(this)
handler.bind(this),
);
}
@ -24,7 +24,7 @@ class Controller {
this.app.post(
path,
checkPermission(this.config, permission),
handler.bind(this)
handler.bind(this),
);
}
@ -32,7 +32,7 @@ class Controller {
this.app.put(
path,
checkPermission(this.config, permission),
handler.bind(this)
handler.bind(this),
);
}
@ -40,7 +40,7 @@ class Controller {
this.app.delete(
path,
checkPermission(this.config, permission),
handler.bind(this)
handler.bind(this),
);
}
@ -49,7 +49,7 @@ class Controller {
path,
checkPermission(this.config, permission),
filehandler,
handler.bind(this)
handler.bind(this),
);
}

View File

@ -1,17 +1,17 @@
'use strict';
const test = require('ava');
const store = require('./../../test/fixtures/store');
const getLogger = require('../../test/fixtures/no-logger');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../test/fixtures/store');
const getLogger = require('../../test/fixtures/no-logger');
const getApp = require('../app');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {
const stores = store.createStores();
const db = stores.db;
const { db } = stores;
const app = getApp({
baseUriPath: '',
stores,

View File

@ -1,12 +1,12 @@
'use strict';
const test = require('ava');
const store = require('./../../test/fixtures/store');
const getLogger = require('../../test/fixtures/no-logger');
const supertest = require('supertest');
const { EventEmitter } = require('events');
const store = require('../../test/fixtures/store');
const getLogger = require('../../test/fixtures/no-logger');
const getApp = require('../app');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
function getSetup() {
@ -40,7 +40,7 @@ test('api defintion', t => {
t.true(admin.uri === '/api/admin');
t.true(client.uri === '/api/client');
t.true(
admin.links['feature-toggles'].uri === '/api/admin/features'
admin.links['feature-toggles'].uri === '/api/admin/features',
);
t.true(client.links.metrics.uri === '/api/client/metrics');
});
@ -56,7 +56,7 @@ test('admin api defintion', t => {
.expect(res => {
t.truthy(res.body);
t.true(
res.body.links['feature-toggles'].uri === '/api/admin/features'
res.body.links['feature-toggles'].uri === '/api/admin/features',
);
});
});

View File

@ -2,12 +2,12 @@
const test = require('ava');
const supertest = require('supertest');
const store = require('./../../test/fixtures/store');
const { EventEmitter } = require('events');
const store = require('../../test/fixtures/store');
const getLogger = require('../../test/fixtures/no-logger');
const getApp = require('../app');
const User = require('../user');
const { EventEmitter } = require('events');
const eventBus = new EventEmitter();
const currentUser = new User({ email: 'test@mail.com' });

View File

@ -20,15 +20,14 @@ async function createApp(options) {
const stores = createStores(options, eventBus);
const secret = await stores.settingStore.get('unleash.secret');
const config = Object.assign(
{
stores,
eventBus,
secret,
logFactory: options.getLogger, // TODO: remove in v4.x
},
options
);
const config = {
stores,
eventBus,
secret,
logFactory: options.getLogger, // TODO: remove in v4.x
...options,
};
const app = getApp(config);
startMonitoring(
@ -36,7 +35,7 @@ async function createApp(options) {
eventBus,
stores.eventStore,
stores.clientMetricsStore,
stores.featureToggleStore
stores.featureToggleStore,
);
if (typeof config.eventHook === 'function') {
@ -54,7 +53,7 @@ async function createApp(options) {
}
const server = app.listen(options.listen, () =>
logger.info('Unleash has started.', server.address())
logger.info('Unleash has started.', server.address()),
);
return new Promise((resolve, reject) => {
@ -65,7 +64,7 @@ async function createApp(options) {
server,
eventBus,
stateService,
})
}),
);
server.on('error', reject);
});

View File

@ -41,7 +41,7 @@ const serverImpl = proxyquire('./server-impl', {
return o;
},
},
'../migrator'() {
'../migrator': function() {
return Promise.resolve();
},
});

View File

@ -3,9 +3,9 @@
const joi = require('@hapi/joi');
const fs = require('fs');
const mime = require('mime');
const YAML = require('js-yaml');
const { featureShema } = require('./routes/admin-api/feature-schema');
const strategySchema = require('./routes/admin-api/strategy-schema');
const YAML = require('js-yaml');
const {
FEATURE_IMPORT,
DROP_FEATURES,
@ -27,7 +27,7 @@ const dataSchema = joi.object().keys({
function readFile(file) {
return new Promise((resolve, reject) =>
fs.readFile(file, (err, v) => (err ? reject(err) : resolve(v)))
fs.readFile(file, (err, v) => (err ? reject(err) : resolve(v))),
);
}
@ -56,7 +56,7 @@ class StateService {
if (importData.features) {
this.logger.info(
`Importing ${importData.features.length} features`
`Importing ${importData.features.length} features`,
);
if (dropBeforeImport) {
this.logger.info(`Dropping existing features`);
@ -72,14 +72,14 @@ class StateService {
type: FEATURE_IMPORT,
createdBy: userName,
data: feature,
})
)
}),
),
);
}
if (importData.strategies) {
this.logger.info(
`Importing ${importData.strategies.length} strategies`
`Importing ${importData.strategies.length} strategies`,
);
if (dropBeforeImport) {
this.logger.info(`Dropping existing strategies`);
@ -95,8 +95,8 @@ class StateService {
type: STRATEGY_IMPORT,
createdBy: userName,
data: strategy,
})
)
}),
),
);
}
}

View File

@ -2,8 +2,8 @@
const test = require('ava');
const store = require('./../test/fixtures/store');
const getLogger = require('./../test/fixtures/no-logger');
const store = require('../test/fixtures/store');
const getLogger = require('../test/fixtures/no-logger');
const StateService = require('./state-service');
const {

View File

@ -8,7 +8,7 @@ function timeout(fn, ms) {
setTimeout(() => {
fn();
resolve();
}, ms)
}, ms),
);
}
@ -28,5 +28,5 @@ test('timer should track the time', async t => {
if (diff > 0.019 && diff < 0.05) {
return t.pass();
}
t.fail();
return t.fail();
});

View File

@ -10,7 +10,7 @@ module.exports = class User {
Joi.string()
.email()
.required(),
'Email'
'Email',
);
this.email = email;
this.name = name;

View File

@ -9,7 +9,7 @@ test('should create user', t => {
t.is(user.email, 'some@email.com');
t.is(
user.imageUrl,
'https://gravatar.com/avatar/d8ffeba65ee5baf57e4901690edc8e1b?size=42&default=retro'
'https://gravatar.com/avatar/d8ffeba65ee5baf57e4901690edc8e1b?size=42&default=retro',
);
});
@ -18,7 +18,7 @@ test('should require email', t => {
() => {
const user = new User(); // eslint-disable-line
},
{ instanceOf: Error }
{ instanceOf: Error },
);
t.is(error.message, 'Email "value" is required');

View File

@ -25,7 +25,7 @@ CREATE TABLE events (
data json
);
`,
callback
callback,
);
};
@ -36,6 +36,6 @@ DROP TABLE events;
DROP TABLE features;
DROP TABLE strategies;
`,
callback
callback,
);
};

View File

@ -3,13 +3,13 @@
exports.up = function(db, callback) {
db.runSql(
'ALTER TABLE strategies ADD "parameters_template" json;',
callback
callback,
);
};
exports.down = function(db, callback) {
db.runSql(
'ALTER TABLE strategies DROP COLUMN "parameters_template";',
callback
callback,
);
};

View File

@ -6,7 +6,7 @@ exports.up = function(db, callback) {
INSERT INTO strategies(name, description)
VALUES ('default', 'Default on/off strategy.');
`,
callback
callback,
);
};
@ -14,6 +14,6 @@ exports.down = function(db, callback) {
db.runSql(
`
DELETE FROM strategies where name='default';`,
callback
callback,
);
};

View File

@ -6,7 +6,7 @@ exports.up = function(db, callback) {
INSERT INTO events(type, created_by, data)
VALUES ('strategy-created', 'migration', '{"name":"default","description":"Default on or off Strategy."}');
`,
callback
callback,
);
};
@ -14,6 +14,6 @@ exports.down = function(db, callback) {
db.runSql(
`
delete from events where type='strategy-created' and data->>'name' = 'default';`,
callback
callback,
);
};

View File

@ -6,7 +6,7 @@ exports.up = function(db, callback) {
UPDATE events SET type='feature-revived' WHERE type='feature-revive';
UPDATE events SET type='feature-archived' WHERE type='feature-archive';
`,
callback
callback,
);
};
@ -16,6 +16,6 @@ exports.down = function(db, callback) {
UPDATE events SET type='feature-revive' WHERE type='feature-revived';
UPDATE events SET type='feature-archive' WHERE type='feature-archived';
`,
callback
callback,
);
};

View File

@ -16,7 +16,7 @@ WHERE f.name = features.name;
ALTER TABLE features DROP COLUMN "strategy_name";
ALTER TABLE features DROP COLUMN "parameters";
`,
callback
callback,
);
};
@ -37,6 +37,6 @@ WHERE f.name = features.name;
--drop new column
ALTER TABLE features DROP COLUMN "strategies";
`,
callback
callback,
);
};

View File

@ -8,7 +8,7 @@ CREATE TABLE client_metrics (
created_at timestamp default now(),
metrics json
);`,
callback
callback,
);
};

View File

@ -10,7 +10,7 @@ exports.up = function(db, callback) {
last_seen timestamp default now(),
created_at timestamp default now()
);`,
callback
callback,
);
};

View File

@ -1,4 +1,5 @@
/* eslint camelcase: "off" */
'use strict';
exports.up = function(db, cb) {
@ -20,7 +21,7 @@ exports.up = function(db, cb) {
url: { type: 'string', length: 255 },
color: { type: 'string', length: 255 },
},
cb
cb,
);
};

View File

@ -1,4 +1,5 @@
/* eslint camelcase: "off" */
'use strict';
const async = require('async');
@ -25,12 +26,12 @@ exports.up = function(db, callback) {
strategy => `
UPDATE strategies
SET parameters='${JSON.stringify(strategy.parameters)}'
WHERE name='${strategy.name}';`
WHERE name='${strategy.name}';`,
)
.join('\n');
db.runSql(updateSQL, cb);
}
},
);
};
@ -40,7 +41,7 @@ exports.up = function(db, callback) {
populateNewData.bind(db),
db.removeColumn.bind(db, 'strategies', 'parameters_template'),
],
callback
callback,
);
};
@ -60,9 +61,9 @@ exports.down = function(db, callback) {
strategy => `
UPDATE strategies
SET parameters_template='${JSON.stringify(
strategy.parameters_template
strategy.parameters_template,
)}'
WHERE name='${strategy.name}';`
WHERE name='${strategy.name}';`,
)
.join('\n');
@ -78,6 +79,6 @@ exports.down = function(db, callback) {
populateOldData.bind(db),
db.removeColumn.bind(db, 'strategies', 'parameters'),
],
callback
callback,
);
};

View File

@ -11,10 +11,10 @@ exports.up = function(db, cb) {
}),
db.runSql.bind(
db,
"UPDATE strategies SET built_in=1 where name='default'"
"UPDATE strategies SET built_in=1 where name='default'",
),
],
cb
cb,
);
};

View File

@ -1,13 +1,13 @@
'use strict';
const strategies = require('./default-strategies.json');
const async = require('async');
const strategies = require('./default-strategies.json');
function insertStrategySQL(strategy) {
return `
INSERT INTO strategies (name, description, parameters, built_in)
SELECT '${strategy.name}', '${strategy.description}', '${JSON.stringify(
strategy.parameters
strategy.parameters,
)}', 1
WHERE
NOT EXISTS (
@ -50,7 +50,7 @@ exports.up = function(db, callback) {
db.runSql.bind(db, insertEventsSQL(s)),
db.runSql.bind(db, insertStrategySQL(s)),
],
cb
cb,
);
});
async.series(insertStrategies, callback);
@ -65,7 +65,7 @@ exports.down = function(db, callback) {
db.runSql.bind(db, removeEventsSQL(s)),
db.runSql.bind(db, removeStrategySQL(s)),
],
cb
cb,
);
});

View File

@ -13,7 +13,7 @@ ALTER TABLE client_instances ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZO
ALTER TABLE client_instances ALTER COLUMN last_seen TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE client_metrics ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;
`,
callback
callback,
);
};

View File

@ -3,13 +3,13 @@
exports.up = function(db, callback) {
db.runSql(
'ALTER TABLE client_instances ADD "sdk_version" varchar(255);',
callback
callback,
);
};
exports.down = function(db, callback) {
db.runSql(
'ALTER TABLE client_instances DROP COLUMN "sdk_version";',
callback
callback,
);
};

View File

@ -6,7 +6,7 @@ exports.up = function(db, callback) {
ALTER TABLE features ADD "variants" json;
ALTER TABLE features ALTER COLUMN "variants" SET DEFAULT '[]';
`,
callback
callback,
);
};

View File

@ -1,13 +1,13 @@
'use strict';
const flexibleRollout = require('./flexible-rollout-strategy.json');
const async = require('async');
const flexibleRollout = require('./flexible-rollout-strategy.json');
function insertStrategySQL(strategy) {
return `
INSERT INTO strategies (name, description, parameters, built_in)
SELECT '${strategy.name}', '${strategy.description}', '${JSON.stringify(
strategy.parameters
strategy.parameters,
)}', 1
WHERE
NOT EXISTS (
@ -49,7 +49,7 @@ exports.up = function(db, callback) {
db.runSql.bind(db, insertEventsSQL(flexibleRollout)),
db.runSql.bind(db, insertStrategySQL(flexibleRollout)),
],
callback
callback,
);
};
@ -59,6 +59,6 @@ exports.down = function(db, callback) {
db.runSql.bind(db, removeEventsSQL(flexibleRollout)),
db.runSql.bind(db, removeStrategySQL(flexibleRollout)),
],
callback
callback,
);
};

View File

@ -1,4 +1,5 @@
/* eslint camelcase: "off" */
'use strict';
const async = require('async');
@ -25,10 +26,10 @@ exports.up = function(db, cb) {
INSERT INTO context_fields(name, description, sort_order) VALUES('environment', 'Allows you to constrain on application environment', 0);
INSERT INTO context_fields(name, description, sort_order) VALUES('userId', 'Allows you to constrain on userId', 1);
INSERT INTO context_fields(name, description, sort_order) VALUES('appName', 'Allows you to constrain on application name', 2);
`
`,
),
],
cb
cb,
);
};

View File

@ -1,4 +1,5 @@
/* eslint camelcase: "off" */
'use strict';
exports.up = function(db, cb) {
@ -13,7 +14,7 @@ exports.up = function(db, cb) {
},
content: { type: 'json' },
},
cb
cb,
);
};

View File

@ -1,4 +1,5 @@
/* eslint camelcase: "off" */
'use strict';
const crypto = require('crypto');
@ -12,7 +13,7 @@ exports.up = function(db, cb) {
`
INSERT INTO settings(name, content)
VALUES('${settingsName}', '${JSON.stringify(secret)}')`,
cb
cb,
);
};

View File

@ -5,7 +5,7 @@ require('db-migrate-shared').log.setLogLevel('error');
const { getInstance } = require('db-migrate');
function migrateDb({ db, databaseSchema = 'public' }) {
const custom = Object.assign({}, db, { schema: databaseSchema });
const custom = { ...db, schema: databaseSchema };
const dbmigrate = getInstance(true, {
cwd: __dirname,
config: { custom },

View File

@ -69,6 +69,7 @@
"cookie-session": "^2.0.0-beta.3",
"db-migrate": "^0.11.6",
"db-migrate-pg": "^1.0.0",
"db-migrate-shared": "^1.2.0",
"deep-diff": "^1.0.2",
"deepmerge": "^4.2.2",
"errorhandler": "^1.5.1",
@ -96,15 +97,17 @@
"ava": "^3.7.0",
"coveralls": "^3.0.6",
"eslint": "^6.8.0",
"eslint-config-finn": "^3.0.1",
"eslint-config-finn-prettier": "^3.0.2",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-config-prettier": "^6.10.1",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-prettier": "^3.1.3",
"husky": "^4.2.3",
"lint-staged": "^10.0.7",
"lolex": "^6.0.0",
"nyc": "^15.0.0",
"passport": "^0.4.1",
"passport-google-auth": "^1.0.2",
"prettier": "^1.18.2",
"prettier": "^1.19.1",
"proxyquire": "^2.1.3",
"superagent": "^5.1.0",
"supertest": "^4.0.2",

Some files were not shown because too many files have changed in this diff Show More