mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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:
		
							parent
							
								
									9676165de9
								
							
						
					
					
						commit
						037b8eacd3
					
				@ -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)
 | 
				
			||||||
 | 
				
			|||||||
@ -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,
 | 
				
			||||||
 | 
				
			|||||||
@ -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,
 | 
				
			||||||
 | 
				
			|||||||
@ -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[]>;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								src/test/fixtures/fake-project-store.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								src/test/fixtures/fake-project-store.ts
									
									
									
									
										vendored
									
									
								
							@ -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.');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user