UNPKG

unleash-server

Version:

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

307 lines • 11.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const notfound_error_1 = __importDefault(require("../error/notfound-error")); const constants_1 = require("../util/constants"); const metrics_helper_1 = __importDefault(require("../util/metrics-helper")); const metric_events_1 = require("../metric-events"); const COLUMNS = [ 'id', 'name', 'description', 'created_at', 'health', 'updated_at', ]; const TABLE = 'projects'; class ProjectStore { constructor(db, eventBus, getLogger, flagResolver) { this.db = db; this.logger = getLogger('project-store.ts'); this.timer = (action) => metrics_helper_1.default.wrapTimer(eventBus, metric_events_1.DB_TIME, { store: 'project', action, }); this.flagResolver = flagResolver; } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types fieldToRow(data) { return { id: data.id, name: data.name, description: data.description, }; } destroy() { } async exists(id) { const result = await this.db.raw(`SELECT EXISTS(SELECT 1 FROM ${TABLE} WHERE id = ?) AS present`, [id]); const { present } = result.rows[0]; return present; } async getProjectsWithCounts(query, userId) { const projectTimer = this.timer('getProjectsWithCount'); let projects = this.db(TABLE) .leftJoin('features', 'features.project', 'projects.id') .orderBy('projects.name', 'asc'); if (query) { projects = projects.where(query); } let selectColumns = [ this.db.raw('projects.id, projects.name, projects.description, projects.health, projects.updated_at, count(features.name) AS number_of_features'), ]; let groupByColumns = ['projects.id']; if (userId && this.flagResolver.isEnabled('favorites')) { projects = projects.leftJoin(`favorite_projects`, function () { this.on('favorite_projects.project', 'projects.id').andOnVal('favorite_projects.user_id', '=', userId); }); selectColumns = [ ...selectColumns, this.db.raw('favorite_projects.project is not null as favorite'), ]; groupByColumns = [...groupByColumns, 'favorite_projects.project']; } const projectAndFeatureCount = await projects .select(selectColumns) .groupBy(groupByColumns); const projectsWithFeatureCount = projectAndFeatureCount.map(this.mapProjectWithCountRow); projectTimer(); const memberTimer = this.timer('getMemberCount'); const memberCount = await this.getMembersCount(); memberTimer(); const memberMap = new Map(memberCount.map((c) => [c.project, Number(c.count)])); return projectsWithFeatureCount.map((r) => { return { ...r, memberCount: memberMap.get(r.id) }; }); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types mapProjectWithCountRow(row) { return { name: row.name, id: row.id, description: row.description, health: row.health, favorite: row.favorite, featureCount: Number(row.number_of_features) || 0, memberCount: Number(row.number_of_users) || 0, updatedAt: row.updated_at, }; } async getAll(query = {}) { const rows = await this.db .select(COLUMNS) .from(TABLE) .where(query) .orderBy('name', 'asc'); return rows.map(this.mapRow); } async get(id) { return this.db .first(COLUMNS) .from(TABLE) .where({ id }) .then(this.mapRow); } async hasProject(id) { const result = await this.db.raw(`SELECT EXISTS(SELECT 1 FROM ${TABLE} WHERE id = ?) AS present`, [id]); const { present } = result.rows[0]; return present; } async updateHealth(healthUpdate) { await this.db(TABLE) .where({ id: healthUpdate.id }) .update({ health: healthUpdate.health, updated_at: new Date() }); } async create(project) { const row = await this.db(TABLE) .insert(this.fieldToRow(project)) .returning('*'); return this.mapRow(row[0]); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types async update(data) { try { await this.db(TABLE) .where({ id: data.id }) .update(this.fieldToRow(data)); } catch (err) { this.logger.error('Could not update project, error: ', err); } } async importProjects(projects, environments) { const rows = await this.db(TABLE) .insert(projects.map(this.fieldToRow)) .returning(COLUMNS) .onConflict('id') .ignore(); if (environments && rows.length > 0) { environments.forEach((env) => { projects.forEach(async (project) => { await this.addEnvironmentToProject(project.id, env.name); }); }); return rows.map(this.mapRow); } return []; } async addDefaultEnvironment(projects) { const environments = projects.map((p) => ({ project_id: p.id, environment_name: constants_1.DEFAULT_ENV, })); await this.db('project_environments') .insert(environments) .onConflict(['project_id', 'environment_name']) .ignore(); } async deleteAll() { await this.db(TABLE).del(); } async delete(id) { try { await this.db(TABLE).where({ id }).del(); } catch (err) { this.logger.error('Could not delete project, error: ', err); } } async getProjectLinksForEnvironments(environments) { let rows = await this.db('project_environments') .select(['project_id', 'environment_name']) .whereIn('environment_name', environments); return rows.map(this.mapLinkRow); } async deleteEnvironmentForProject(id, environment) { await this.db('project_environments') .where({ project_id: id, environment_name: environment, }) .del(); } async addEnvironmentToProject(id, environment) { await this.db('project_environments') .insert({ project_id: id, environment_name: environment, }) .onConflict(['project_id', 'environment_name']) .ignore(); } async addEnvironmentToProjects(environment, projects) { const rows = await Promise.all(projects.map(async (projectId) => { return { project_id: projectId, environment_name: environment, }; })); await this.db('project_environments') .insert(rows) .onConflict(['project_id', 'environment_name']) .ignore(); } async getEnvironmentsForProject(id) { return this.db('project_environments') .where({ project_id: id, }) .innerJoin('environments', 'project_environments.environment_name', 'environments.name') .orderBy('environments.sort_order', 'asc') .orderBy('project_environments.environment_name', 'asc') .pluck('project_environments.environment_name'); } async getMembersCount() { const members = await this.db .select('project') .from((db) => { db.select('user_id', 'project') .from('role_user') .leftJoin('roles', 'role_user.role_id', 'roles.id') .where((builder) => builder.whereNot('type', 'root')) .union((queryBuilder) => { queryBuilder .select('user_id', 'project') .from('group_role') .leftJoin('group_user', 'group_user.group_id', 'group_role.group_id'); }) .as('query'); }) .groupBy('project') .count('user_id'); return members; } async getProjectsByUser(userId) { const members = await this.db .from((db) => { db.select('project') .from('role_user') .leftJoin('roles', 'role_user.role_id', 'roles.id') .where('type', 'root') .andWhere('name', 'Editor') .andWhere('user_id', userId) .union((queryBuilder) => { queryBuilder .select('project') .from('group_role') .leftJoin('group_user', 'group_user.group_id', 'group_role.group_id') .where('user_id', userId); }) .as('query'); }) .pluck('project'); return members; } async getMembersCountByProject(projectId) { const members = await this.db .from((db) => { db.select('user_id') .from('role_user') .leftJoin('roles', 'role_user.role_id', 'roles.id') .where((builder) => builder .where('project', projectId) .whereNot('type', 'root')) .union((queryBuilder) => { queryBuilder .select('user_id') .from('group_role') .leftJoin('group_user', 'group_user.group_id', 'group_role.group_id') .where('project', projectId); }) .as('query'); }) .count() .first(); return Number(members.count); } async count() { return this.db .from(TABLE) .count('*') .then((res) => Number(res[0].count)); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types mapLinkRow(row) { return { environmentName: row.environment_name, projectId: row.project_id, }; } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types mapRow(row) { if (!row) { throw new notfound_error_1.default('No project found'); } return { id: row.id, name: row.name, description: row.description, createdAt: row.created_at, health: row.health || 100, updatedAt: row.updated_at || new Date(), }; } } exports.default = ProjectStore; //# sourceMappingURL=project-store.js.map