2018-09-11 15:08:34 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-03-07 06:58:14 -05:00
|
|
|
class MembersFinder
|
|
|
|
attr_reader :project, :current_user, :group
|
|
|
|
|
|
|
|
def initialize(project, current_user)
|
|
|
|
@project = project
|
|
|
|
@current_user = current_user
|
|
|
|
@group = project.group
|
|
|
|
end
|
|
|
|
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2018-06-07 16:54:24 -04:00
|
|
|
def execute(include_descendants: false)
|
2017-03-07 06:58:14 -05:00
|
|
|
project_members = project.project_members
|
|
|
|
project_members = project_members.non_invite unless can?(current_user, :admin_project, project)
|
|
|
|
|
|
|
|
if group
|
2018-08-27 11:31:01 -04:00
|
|
|
group_members = GroupMembersFinder.new(group).execute(include_descendants: include_descendants) # rubocop: disable CodeReuse/Finder
|
2018-02-20 10:29:16 -05:00
|
|
|
group_members = group_members.non_invite
|
2017-03-07 06:58:14 -05:00
|
|
|
|
2018-09-11 11:31:34 -04:00
|
|
|
union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false) # rubocop: disable Gitlab/Union
|
2017-03-07 06:58:14 -05:00
|
|
|
|
2018-02-19 12:28:11 -05:00
|
|
|
sql = distinct_on(union)
|
2018-02-16 15:43:09 -05:00
|
|
|
|
2018-02-20 08:12:56 -05:00
|
|
|
Member.includes(:user).from("(#{sql}) AS #{Member.table_name}")
|
2018-02-16 15:43:09 -05:00
|
|
|
else
|
|
|
|
project_members
|
|
|
|
end
|
2016-12-23 17:29:00 -05:00
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2016-12-23 17:29:00 -05:00
|
|
|
|
2017-03-07 06:58:14 -05:00
|
|
|
def can?(*args)
|
|
|
|
Ability.allowed?(*args)
|
2016-12-23 17:29:00 -05:00
|
|
|
end
|
2018-02-19 12:28:11 -05:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def distinct_on(union)
|
|
|
|
# We're interested in a list of members without duplicates by user_id.
|
|
|
|
# We prefer project members over group members, project members should go first.
|
|
|
|
if Gitlab::Database.postgresql?
|
|
|
|
<<~SQL
|
|
|
|
SELECT DISTINCT ON (user_id, invite_email) member_union.*
|
|
|
|
FROM (#{union.to_sql}) AS member_union
|
2018-02-21 07:54:35 -05:00
|
|
|
ORDER BY user_id,
|
|
|
|
invite_email,
|
|
|
|
CASE
|
|
|
|
WHEN type = 'ProjectMember' THEN 1
|
|
|
|
WHEN type = 'GroupMember' THEN 2
|
|
|
|
ELSE 3
|
|
|
|
END
|
2018-02-19 12:28:11 -05:00
|
|
|
SQL
|
|
|
|
else
|
|
|
|
# Older versions of MySQL do not support window functions (and DISTINCT ON is postgres-specific).
|
|
|
|
<<~SQL
|
|
|
|
SELECT t1.*
|
|
|
|
FROM (#{union.to_sql}) AS t1
|
|
|
|
JOIN (
|
|
|
|
SELECT
|
|
|
|
COALESCE(user_id, -1) AS user_id,
|
|
|
|
COALESCE(invite_email, 'NULL') AS invite_email,
|
|
|
|
MIN(CASE WHEN type = 'ProjectMember' THEN 1 WHEN type = 'GroupMember' THEN 2 ELSE 3 END) AS type_number
|
|
|
|
FROM
|
|
|
|
(#{union.to_sql}) AS t3
|
|
|
|
GROUP BY COALESCE(user_id, -1), COALESCE(invite_email, 'NULL')
|
|
|
|
) AS t2 ON COALESCE(t1.user_id, -1) = t2.user_id
|
|
|
|
AND COALESCE(t1.invite_email, 'NULL') = t2.invite_email
|
|
|
|
AND CASE WHEN t1.type = 'ProjectMember' THEN 1 WHEN t1.type = 'GroupMember' THEN 2 ELSE 3 END = t2.type_number
|
|
|
|
SQL
|
|
|
|
end
|
|
|
|
end
|
2016-12-23 17:29:00 -05:00
|
|
|
end
|