mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-31 01:16:01 +02:00
parent
f4aba80763
commit
9e649118e5
@ -5,6 +5,7 @@ const NotFoundError = require('../error/notfound-error');
|
||||
const COLUMNS = [
|
||||
'app_name',
|
||||
'created_at',
|
||||
'created_by',
|
||||
'updated_at',
|
||||
'description',
|
||||
'strategies',
|
||||
@ -20,6 +21,7 @@ const mapRow = row => ({
|
||||
updatedAt: row.updated_at,
|
||||
description: row.description,
|
||||
strategies: row.strategies,
|
||||
createdBy: row.created_by,
|
||||
url: row.url,
|
||||
color: row.color,
|
||||
icon: row.icon,
|
||||
@ -29,6 +31,8 @@ const remapRow = (input, old = {}) => ({
|
||||
app_name: input.appName,
|
||||
updated_at: input.updatedAt,
|
||||
description: input.description || old.description,
|
||||
created_by: input.createdBy || old.createdBy,
|
||||
announced: input.announced || old.announced || false,
|
||||
url: input.url || old.url,
|
||||
color: input.color || old.color,
|
||||
icon: input.icon || old.icon,
|
||||
@ -118,6 +122,26 @@ class ClientApplicationsDb {
|
||||
? this.getAppsForStrategy(filter.strategyName)
|
||||
: this.getAll();
|
||||
}
|
||||
|
||||
async getUnannounced() {
|
||||
const rows = await this.db(TABLE)
|
||||
.select(COLUMNS)
|
||||
.where('announced', false);
|
||||
return rows.map(mapRow);
|
||||
}
|
||||
|
||||
/** *
|
||||
* Updates all rows that have announced = false to announced =true and returns the rows altered
|
||||
* @return {[app]} - Apps that hadn't been announced
|
||||
*/
|
||||
async setUnannouncedToAnnounced() {
|
||||
const rows = await this.db(TABLE)
|
||||
.update({ announced: true })
|
||||
.where('announced', false)
|
||||
.whereNotNull('announced')
|
||||
.returning(COLUMNS);
|
||||
return rows.map(mapRow);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClientApplicationsDb;
|
||||
|
@ -10,6 +10,7 @@ const { clientRegisterSchema } = require('./register-schema');
|
||||
const { APPLICATION_CREATED } = require('../../event-type');
|
||||
|
||||
const FIVE_SECONDS = 5 * 1000;
|
||||
const FIVE_MINUTES = 5 * 60 * 1000;
|
||||
|
||||
module.exports = class ClientMetricsService {
|
||||
constructor(
|
||||
@ -21,7 +22,11 @@ module.exports = class ClientMetricsService {
|
||||
clientInstanceStore,
|
||||
eventStore,
|
||||
},
|
||||
{ getLogger, bulkInterval = FIVE_SECONDS },
|
||||
{
|
||||
getLogger,
|
||||
bulkInterval = FIVE_SECONDS,
|
||||
announcementInterval: appAnnouncementInterval = FIVE_MINUTES,
|
||||
},
|
||||
) {
|
||||
this.globalCount = 0;
|
||||
this.apps = {};
|
||||
@ -63,6 +68,7 @@ module.exports = class ClientMetricsService {
|
||||
});
|
||||
this.seenClients = {};
|
||||
setInterval(() => this.bulkAdd(), bulkInterval);
|
||||
setInterval(() => this.announceUnannounced(), appAnnouncementInterval);
|
||||
clientMetricsStore.on('metrics', m => this.addPayload(m));
|
||||
}
|
||||
|
||||
@ -78,10 +84,26 @@ module.exports = class ClientMetricsService {
|
||||
});
|
||||
}
|
||||
|
||||
async announceUnannounced() {
|
||||
if (this.clientAppStore) {
|
||||
const appsToAnnounce = await this.clientAppStore.setUnannouncedToAnnounced();
|
||||
if (appsToAnnounce.length > 0) {
|
||||
const events = appsToAnnounce.map(app => {
|
||||
return {
|
||||
type: APPLICATION_CREATED,
|
||||
createdBy: app.createdBy,
|
||||
data: app,
|
||||
};
|
||||
});
|
||||
await this.eventStore.batchStore(events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async registerClient(data, clientIp) {
|
||||
const value = await clientRegisterSchema.validateAsync(data);
|
||||
value.clientIp = clientIp;
|
||||
this.logger.info(`${JSON.stringify(data)}`);
|
||||
value.createdBy = clientIp;
|
||||
this.seenClients[this.clientKey(value)] = value;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
exports.up = function(db, cb) {
|
||||
db.runSql(
|
||||
`
|
||||
ALTER TABLE client_applications ADD COLUMN announced boolean DEFAULT false;
|
||||
UPDATE client_applications SET announced = true;
|
||||
`,
|
||||
cb,
|
||||
);
|
||||
};
|
||||
|
||||
exports.down = function(db, cb) {
|
||||
db.runSql(
|
||||
`
|
||||
ALTER TABLE client_applications DROP COLUMN announced;
|
||||
`,
|
||||
cb,
|
||||
);
|
||||
};
|
||||
|
||||
exports._meta = {
|
||||
version: 1,
|
||||
};
|
@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
exports.up = function(db, cb) {
|
||||
db.runSql(
|
||||
`
|
||||
ALTER TABLE client_applications ADD COLUMN created_by TEXT;
|
||||
`,
|
||||
cb,
|
||||
);
|
||||
};
|
||||
|
||||
exports.down = function(db, cb) {
|
||||
db.runSql('ALTER TABLE client_applications DROP COLUMN created_by;', cb);
|
||||
};
|
||||
|
||||
exports._meta = {
|
||||
version: 1,
|
||||
};
|
@ -24,17 +24,20 @@
|
||||
"applications": [
|
||||
{
|
||||
"appName": "demo-app-1",
|
||||
"strategies": ["default"]
|
||||
"strategies": ["default"],
|
||||
"announced": true
|
||||
},
|
||||
{
|
||||
"appName": "demo-app-2",
|
||||
"strategies": ["default", "extra"],
|
||||
"description": "hello"
|
||||
"description": "hello",
|
||||
"announced": true
|
||||
},
|
||||
{
|
||||
"appName": "deletable-app",
|
||||
"strategies": ["default"],
|
||||
"description": "Some desc"
|
||||
"description": "Some desc",
|
||||
"announced": true
|
||||
}
|
||||
],
|
||||
"clientInstances": [
|
||||
|
58
src/test/e2e/services/client-metrics-service.e2e.test.js
Normal file
58
src/test/e2e/services/client-metrics-service.e2e.test.js
Normal file
@ -0,0 +1,58 @@
|
||||
const test = require('ava');
|
||||
const faker = require('faker');
|
||||
const dbInit = require('../helpers/database-init');
|
||||
const getLogger = require('../../fixtures/no-logger');
|
||||
const ClientMetricsService = require('../../../lib/services/client-metrics');
|
||||
const { APPLICATION_CREATED } = require('../../../lib/event-type');
|
||||
|
||||
let stores;
|
||||
let clientMetricsService;
|
||||
|
||||
test.before(async () => {
|
||||
const db = await dbInit('client_metrics_service_serial', getLogger);
|
||||
stores = db.stores;
|
||||
clientMetricsService = new ClientMetricsService(stores, {
|
||||
getLogger,
|
||||
bulkInterval: 500,
|
||||
announcementInterval: 2000,
|
||||
});
|
||||
});
|
||||
|
||||
test.after(async () => {
|
||||
await stores.db.destroy();
|
||||
});
|
||||
test.serial('Apps registered should be announced', async t => {
|
||||
t.plan(3);
|
||||
const clientRegistration = {
|
||||
appName: faker.internet.domainName(),
|
||||
instanceId: faker.random.uuid(),
|
||||
strategies: ['default'],
|
||||
started: Date.now(),
|
||||
interval: faker.random.number(),
|
||||
icon: '',
|
||||
description: faker.company.catchPhrase(),
|
||||
color: faker.internet.color(),
|
||||
};
|
||||
const differentClient = {
|
||||
appName: faker.lorem.slug(2),
|
||||
instanceId: faker.random.uuid(),
|
||||
strategies: ['default'],
|
||||
started: Date.now(),
|
||||
interval: faker.random.number(),
|
||||
icon: '',
|
||||
description: faker.company.catchPhrase(),
|
||||
color: faker.internet.color(),
|
||||
};
|
||||
await clientMetricsService.registerClient(clientRegistration, '127.0.0.1');
|
||||
await clientMetricsService.registerClient(differentClient, '127.0.0.1');
|
||||
await new Promise(res => setTimeout(res, 1200));
|
||||
const first = await stores.clientApplicationsStore.getUnannounced();
|
||||
t.is(first.length, 2);
|
||||
await clientMetricsService.registerClient(clientRegistration, '127.0.0.1');
|
||||
await new Promise(res => setTimeout(res, 2000));
|
||||
const second = await stores.clientApplicationsStore.getUnannounced();
|
||||
t.is(second.length, 0);
|
||||
const events = await stores.eventStore.getEvents();
|
||||
const appCreatedEvents = events.filter(e => e.type === APPLICATION_CREATED);
|
||||
t.is(appCreatedEvents.length, 2);
|
||||
});
|
Loading…
Reference in New Issue
Block a user