'use strict'; const Controller = require('../controller'); const ClientMetrics = require('../../client-metrics'); const schema = require('./metrics-schema'); const { UPDATE_APPLICATION } = require('../../permissions'); class MetricsController extends Controller { constructor(config) { super(config); this.logger = config.getLogger('/admin-api/metrics.js'); const { clientMetricsStore, clientInstanceStore, clientApplicationsStore, strategyStore, featureToggleStore, } = config.stores; this.metrics = new ClientMetrics(clientMetricsStore); this.clientInstanceStore = clientInstanceStore; this.clientApplicationsStore = clientApplicationsStore; this.strategyStore = strategyStore; this.featureToggleStore = featureToggleStore; this.get('/seen-toggles', this.getSeenToggles); this.get('/seen-apps', this.getSeenApps); this.get('/feature-toggles', this.getFeatureToggles); this.get('/feature-toggles/:name', this.getFeatureToggle); this.post( '/applications/:appName', this.createApplication, UPDATE_APPLICATION, ); this.get('/applications/', this.getApplications); this.get('/applications/:appName', this.getApplication); } getSeenToggles(req, res) { const seenAppToggles = this.metrics.getAppsWithToggles(); res.json(seenAppToggles); } async getSeenApps(req, res) { 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; }, {}); Object.keys(seenApps).forEach(key => { seenApps[key] = seenApps[key].map(entry => { if (metaData[entry.appName]) { return { ...entry, ...metaData[entry.appName] }; } return entry; }); }); res.json(seenApps); } getFeatureToggles(req, res) { res.json(this.metrics.getTogglesMetrics()); } getFeatureToggle(req, res) { const { name } = req.params; const data = this.metrics.getTogglesMetrics(); const lastHour = data.lastHour[name] || {}; const lastMinute = data.lastMinute[name] || {}; res.json({ lastHour, lastMinute, }); } async createApplication(req, res) { const input = { ...req.body, appName: req.params.appName }; const { value: applicationData, error } = schema.validate(input); if (error) { this.logger.warn('Invalid application data posted', error); return res.status(400).json(error); } try { await this.clientApplicationsStore.upsert(applicationData); return res.status(202).end(); } catch (err) { this.logger.error(err); return res.status(500).end(); } } async getApplications(req, res) { try { const applications = await this.clientApplicationsStore.getApplications( req.query, ); res.json({ applications }); } catch (err) { this.logger.error(err); res.status(500).end(); } } async getApplication(req, res) { const { appName } = req.params; const seenToggles = this.metrics.getSeenTogglesByAppName(appName); try { const [ application, instances, strategies, features, ] = await Promise.all([ this.clientApplicationsStore.getApplication(appName), this.clientInstanceStore.getByAppName(appName), this.strategyStore.getStrategies(), this.featureToggleStore.getFeatures(), ]); const appDetails = { appName: application.appName, createdAt: application.createdAt, description: application.description, url: application.url, color: application.color, icon: application.icon, strategies: application.strategies.map(name => { const found = strategies.find(f => f.name === name); return found || { name, notFound: true }; }), instances, seenToggles: seenToggles.map(name => { const found = features.find(f => f.name === name); return found || { name, notFound: true }; }), links: { self: `/api/applications/${application.appName}`, }, }; res.json(appDetails); } catch (err) { this.logger.error(err); res.status(500).end(); } } } module.exports = MetricsController;