UNPKG

unleash-server

Version:

Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.

195 lines • 6.24 kB
import { subDays } from 'date-fns'; import metricsHelper from '../util/metrics-helper.js'; import { DB_TIME } from '../metric-events.js'; const COLUMNS = [ 'app_name', 'instance_id', 'sdk_version', 'client_ip', 'last_seen', 'created_at', 'environment', ]; const TABLE = 'client_instances'; const mapRow = (row) => ({ appName: row.app_name, instanceId: row.instance_id, sdkVersion: row.sdk_version, sdkType: row.sdk_type, clientIp: row.client_ip, lastSeen: row.last_seen, createdAt: row.created_at, environment: row.environment, }); const mapToDb = (client) => { const temp = { app_name: client.appName, instance_id: client.instanceId, sdk_version: client.sdkVersion, sdk_type: client.sdkType, client_ip: client.clientIp, last_seen: client.lastSeen || 'now()', environment: client.environment, }; const result = {}; for (const [key, value] of Object.entries(temp)) { if (value !== undefined) { result[key] = value; } } return result; }; export default class ClientInstanceStore { constructor(db, eventBus, getLogger) { this.db = db; this.eventBus = eventBus; this.logger = getLogger('client-instance-store.ts'); this.metricTimer = (action) => metricsHelper.wrapTimer(eventBus, DB_TIME, { store: 'instance', action, }); } async removeOldInstances() { const rows = await this.db(TABLE) .whereRaw("last_seen < now() - interval '1 days'") .del(); if (rows > 0) { this.logger.debug(`Deleted ${rows} instances`); } } async bulkUpsert(instances) { const stopTimer = this.metricTimer('bulkUpsert'); const rows = instances.map(mapToDb); await this.db(TABLE) .insert(rows) .onConflict(['app_name', 'instance_id', 'environment']) .merge(); stopTimer(); } async delete({ appName, instanceId, }) { await this.db(TABLE) .where({ app_name: appName, instance_id: instanceId, }) .del(); } async deleteAll() { await this.db(TABLE).del(); } async get({ appName, instanceId, }) { const row = await this.db(TABLE) .where({ app_name: appName, instance_id: instanceId, }) .first(); return mapRow(row); } async exists({ appName, instanceId, }) { const result = await this.db.raw(`SELECT EXISTS (SELECT 1 FROM ${TABLE} WHERE app_name = ? AND instance_id = ?) AS present`, [appName, instanceId]); const { present } = result.rows[0]; return present; } async upsert(details) { const stopTimer = this.metricTimer('insert'); await this.db(TABLE) .insert(mapToDb(details)) .onConflict(['app_name', 'instance_id', 'environment']) .merge(); stopTimer(); } async getAll() { const stopTimer = this.metricTimer('getAll'); const rows = await this.db .select(COLUMNS) .from(TABLE) .orderBy('last_seen', 'desc'); const toggles = rows.map(mapRow); stopTimer(); return toggles; } async getByAppName(appName) { const rows = await this.db .select() .from(TABLE) .where('app_name', appName) .orderBy('last_seen', 'desc'); return rows.map(mapRow); } async getRecentByAppNameAndEnvironment(appName, environment) { const rows = await this.db .select() .from(TABLE) .where('app_name', appName) .where('environment', environment) .whereRaw("last_seen >= NOW() - INTERVAL '24 hours'") .orderBy('last_seen', 'desc') .limit(1000); return rows.map(mapRow); } async getBySdkName(sdkName) { const sdkPrefix = `${sdkName}%`; const rows = await this.db .select() .from(TABLE) .whereLike('sdk_version', sdkPrefix) .orderBy('last_seen', 'desc'); return rows.map(mapRow); } async groupApplicationsBySdk() { const rows = await this.db .select([ 'sdk_version as sdkVersion', this.db.raw('ARRAY_AGG(DISTINCT app_name) as applications'), ]) .from(TABLE) .groupBy('sdk_version'); return rows; } async groupApplicationsBySdkAndProject(projectId) { const rows = await this.db .with('instances', this.db .select('app_name', 'sdk_version') .distinct() .from('client_instances')) .select([ 'i.sdk_version as sdkVersion', this.db.raw('ARRAY_AGG(DISTINCT cme.app_name) as applications'), ]) .from('client_metrics_env as cme') .leftJoin('features as f', 'f.name', 'cme.feature_name') .leftJoin('instances as i', 'i.app_name', 'cme.app_name') .where('f.project', projectId) .groupBy('i.sdk_version'); return rows; } async getDistinctApplications() { const rows = await this.db .distinct('app_name') .select(['app_name']) .from(TABLE) .orderBy('app_name', 'desc'); return rows.map((r) => r.app_name); } async getDistinctApplicationsCount(daysBefore) { const query = this.db .from((qb) => qb .select('app_name') .from(TABLE) .modify((qb) => { if (daysBefore) { qb.where('last_seen', '>', subDays(new Date(), daysBefore)); } }) .groupBy('app_name') .as('subquery')) .count('* as count'); return query.then((res) => Number(res[0].count)); } async deleteForApplication(appName) { return this.db(TABLE).where('app_name', appName).del(); } destroy() { } } //# sourceMappingURL=client-instance-store.js.map