unleash-server
Version:
Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.
123 lines • 5.64 kB
JavaScript
import metricsHelper from '../util/metrics-helper.js';
import { DB_TIME } from '../metric-events.js';
const TABLE = 'project_stats';
const PROJECT_STATS_COLUMNS = [
'avg_time_to_prod_current_window',
'project',
'features_created_current_window',
'features_created_past_window',
'features_archived_current_window',
'features_archived_past_window',
'project_changes_current_window',
'project_changes_past_window',
'project_members_added_current_window',
];
class ProjectStatsStore {
constructor(db, eventBus, _getLogger) {
this.db = db;
this.timer = (action) => metricsHelper.wrapTimer(eventBus, DB_TIME, {
store: 'project_stats',
action,
});
}
async updateProjectStats(projectId, status) {
await this.db(TABLE)
.insert({
avg_time_to_prod_current_window: status.avgTimeToProdCurrentWindow,
project: projectId,
features_created_current_window: status.createdCurrentWindow,
features_created_past_window: status.createdPastWindow,
features_archived_current_window: status.archivedCurrentWindow,
features_archived_past_window: status.archivedPastWindow,
project_changes_current_window: status.projectActivityCurrentWindow,
project_changes_past_window: status.projectActivityPastWindow,
project_members_added_current_window: status.projectMembersAddedCurrentWindow,
})
.onConflict('project')
.merge();
}
async getProjectStats(projectId) {
const row = await this.db(TABLE)
.select(PROJECT_STATS_COLUMNS)
.where({ project: projectId })
.first();
return this.mapRow(row);
}
mapRow(row) {
if (!row) {
return {
avgTimeToProdCurrentWindow: 0,
createdCurrentWindow: 0,
createdPastWindow: 0,
archivedCurrentWindow: 0,
archivedPastWindow: 0,
projectActivityCurrentWindow: 0,
projectActivityPastWindow: 0,
projectMembersAddedCurrentWindow: 0,
};
}
return {
avgTimeToProdCurrentWindow: row.avg_time_to_prod_current_window,
createdCurrentWindow: row.features_created_current_window,
createdPastWindow: row.features_created_past_window,
archivedCurrentWindow: row.features_archived_current_window,
archivedPastWindow: row.features_archived_past_window,
projectActivityCurrentWindow: row.project_changes_current_window,
projectActivityPastWindow: row.project_changes_past_window,
projectMembersAddedCurrentWindow: row.project_members_added_current_window,
};
}
// we're not calculating time difference in a DB as it requires specialized
// time aware libraries
async getTimeToProdDates(projectId) {
const stopTimer = this.timer('getTimeToProdDates');
const result = await this.db
.select('events.feature_name')
// select only first enabled event, distinct works with orderBy
.distinctOn('events.feature_name')
.select(this.db.raw('events.created_at as enabled, features.created_at as created'))
.from('events')
.innerJoin('environments', 'environments.name', '=', 'events.environment')
.innerJoin('features', 'features.name', '=', 'events.feature_name')
.where('events.type', '=', 'feature-environment-enabled')
.where('environments.type', '=', 'production')
.where('features.type', '=', 'release')
// exclude events for features that were previously deleted
.where(this.db.raw('events.created_at > features.created_at'))
.where('features.project', '=', projectId)
.orderBy('events.feature_name')
// first enabled event
.orderBy('events.created_at', 'asc');
stopTimer();
return result;
}
async getTimeToProdDatesForFeatureToggles(projectId, featureToggleNames) {
const result = await this.db
.select('events.feature_name')
.distinctOn('events.feature_name')
.select(this.db.raw('events.created_at as enabled, features.created_at as created'))
.from('events')
.innerJoin('environments', 'environments.name', '=', 'events.environment')
.innerJoin('features', 'features.name', '=', 'events.feature_name')
.whereIn('events.feature_name', featureToggleNames)
.where('events.type', '=', 'feature-environment-enabled')
.where('environments.type', '=', 'production')
.where('features.type', '=', 'release')
.where(this.db.raw('events.created_at > features.created_at'))
.where('features.project', '=', projectId)
.orderBy('events.feature_name')
.orderBy('events.created_at', 'asc');
const timeDifferenceData = result.map((row) => {
const enabledDate = new Date(row.enabled).getTime();
const createdDate = new Date(row.created).getTime();
const timeDifferenceInDays = Math.floor((enabledDate - createdDate) / (1000 * 60 * 60 * 24));
return {
name: row.feature_name,
timeToProduction: timeDifferenceInDays,
};
});
return timeDifferenceData;
}
}
export default ProjectStatsStore;
//# sourceMappingURL=project-stats-store.js.map