1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-20 00:08:02 +01:00

Wrong number on the project page regarding members in that project (#1917)

* Fix project member count

* Fix

* Add editors to projects
This commit is contained in:
sjaanus 2022-08-17 12:05:41 +03:00 committed by GitHub
parent 9676165de9
commit 037b8eacd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 25 deletions

View File

@ -29,6 +29,11 @@ export interface IEnvironmentProjectLink {
projectId: string; projectId: string;
} }
export interface IProjectMembersCount {
count: number;
project: string;
}
class ProjectStore implements IProjectStore { class ProjectStore implements IProjectStore {
private db: Knex; private db: Knex;
@ -89,12 +94,11 @@ class ProjectStore implements IProjectStore {
); );
projectTimer(); projectTimer();
const memberTimer = this.timer('getMemberCount'); const memberTimer = this.timer('getMemberCount');
const memberCount = await this.db.raw(
`SELECT count(role_id) as member_count, project FROM role_user GROUP BY project`, const memberCount = await this.getMembersCount();
);
memberTimer(); memberTimer();
const memberMap = new Map<string, number>( const memberMap = new Map<string, number>(
memberCount.rows.map((c) => [c.project, Number(c.member_count)]), memberCount.map((c) => [c.project, Number(c.count)]),
); );
return projectsWithFeatureCount.map((r) => { return projectsWithFeatureCount.map((r) => {
return { ...r, memberCount: memberMap.get(r.id) }; return { ...r, memberCount: memberMap.get(r.id) };
@ -247,23 +251,75 @@ class ProjectStore implements IProjectStore {
.pluck('project_environments.environment_name'); .pluck('project_environments.environment_name');
} }
async getMembers(projectId: string): Promise<number> { async getMembersCount(): Promise<IProjectMembersCount[]> {
const rolesFromProject = this.db('role_permission') const members = await this.db
.select('role_id') .select('project')
.distinct(); .from((db) => {
db.select('user_id', 'project')
const numbers = await this.db('role_user') .from('role_user')
.countDistinct('user_id as members') .leftJoin('roles', 'role_user.role_id', 'roles.id')
.where('project', projectId) .where((builder) => builder.whereNot('type', 'root'))
.whereIn('role_id', rolesFromProject) .union((queryBuilder) => {
.first(); queryBuilder
const { members } = numbers; .select('user_id', 'project')
if (typeof members === 'string') { .from('group_role')
return parseInt(members, 10); .leftJoin(
} 'group_user',
'group_user.group_id',
'group_role.group_id',
);
})
.union((queryBuilder) => {
queryBuilder
.select('user_id', 'projects.name as project')
.from('role_user')
.leftJoin('roles', 'role_user.role_id', 'roles.id')
.crossJoin('projects')
.where((builder) =>
builder
.where('type', 'root')
.where('roles.name', 'Editor'),
);
})
.as('query');
})
.groupBy('project')
.count('user_id');
return members; return members;
} }
async getMembersCountByProject(projectId?: string): Promise<number> {
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'),
)
.orWhere((builder) =>
builder.where('type', 'root').where('name', 'Editor'),
)
.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(): Promise<number> { async count(): Promise<number> {
return this.db return this.db
.from(TABLE) .from(TABLE)

View File

@ -67,7 +67,9 @@ export default class ProjectHealthService {
projectId, projectId,
archived, archived,
); );
const members = await this.projectStore.getMembers(projectId); const members = await this.projectStore.getMembersCountByProject(
projectId,
);
return { return {
name: project.name, name: project.name,
description: project.description, description: project.description,

View File

@ -573,7 +573,7 @@ export default class ProjectService {
} }
async getMembers(projectId: string): Promise<number> { async getMembers(projectId: string): Promise<number> {
return this.store.getMembers(projectId); return this.store.getMembersCountByProject(projectId);
} }
async getProjectOverview( async getProjectOverview(
@ -588,7 +588,7 @@ export default class ProjectService {
projectId, projectId,
archived, archived,
); );
const members = await this.store.getMembers(projectId); const members = await this.store.getMembersCountByProject(projectId);
return { return {
name: project.name, name: project.name,
environments, environments,

View File

@ -1,4 +1,7 @@
import { IEnvironmentProjectLink } from '../../db/project-store'; import {
IEnvironmentProjectLink,
IProjectMembersCount,
} from '../../db/project-store';
import { IProject, IProjectWithCount } from '../model'; import { IProject, IProjectWithCount } from '../model';
import { Store } from './store'; import { Store } from './store';
@ -32,7 +35,8 @@ export interface IProjectStore extends Store<IProject, string> {
addEnvironmentToProject(id: string, environment: string): Promise<void>; addEnvironmentToProject(id: string, environment: string): Promise<void>;
deleteEnvironmentForProject(id: string, environment: string): Promise<void>; deleteEnvironmentForProject(id: string, environment: string): Promise<void>;
getEnvironmentsForProject(id: string): Promise<string[]>; getEnvironmentsForProject(id: string): Promise<string[]>;
getMembers(projectId: string): Promise<number>; getMembersCountByProject(projectId: string): Promise<number>;
getMembersCount(): Promise<IProjectMembersCount[]>;
getProjectsWithCounts(query?: IProjectQuery): Promise<IProjectWithCount[]>; getProjectsWithCounts(query?: IProjectQuery): Promise<IProjectWithCount[]>;
count(): Promise<number>; count(): Promise<number>;
getAll(query?: IProjectQuery): Promise<IProject[]>; getAll(query?: IProjectQuery): Promise<IProject[]>;

View File

@ -5,7 +5,10 @@ import {
} from '../../lib/types/stores/project-store'; } from '../../lib/types/stores/project-store';
import { IProject, IProjectWithCount } from '../../lib/types/model'; import { IProject, IProjectWithCount } from '../../lib/types/model';
import NotFoundError from '../../lib/error/notfound-error'; import NotFoundError from '../../lib/error/notfound-error';
import { IEnvironmentProjectLink } from 'lib/db/project-store'; import {
IEnvironmentProjectLink,
IProjectMembersCount,
} from 'lib/db/project-store';
export default class FakeProjectStore implements IProjectStore { export default class FakeProjectStore implements IProjectStore {
projects: IProject[] = []; projects: IProject[] = [];
@ -99,7 +102,7 @@ export default class FakeProjectStore implements IProjectStore {
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
async getMembers(projectId: string): Promise<number> { async getMembersCountByProject(projectId: string): Promise<number> {
return Promise.resolve(0); return Promise.resolve(0);
} }
@ -120,4 +123,8 @@ export default class FakeProjectStore implements IProjectStore {
this.projects.find((p) => p.id === healthUpdate.id).health = this.projects.find((p) => p.id === healthUpdate.id).health =
healthUpdate.health; healthUpdate.health;
} }
getMembersCount(): Promise<IProjectMembersCount[]> {
throw new Error('Method not implemented.');
}
} }