* Fix username combobox tries to load all users * Add the limit param to the users_query * Add a comment
134 lines
5 KiB
Ruby
134 lines
5 KiB
Ruby
module Admin
|
|
class UsersQuery
|
|
SEARCH_CLAUSE = "users.name ILIKE :search OR " \
|
|
"users.email ILIKE :search OR " \
|
|
"users.username ILIKE :search".freeze
|
|
|
|
# @api public
|
|
# @param relation [ActiveRecord::Relation<User>]
|
|
# @param identifier [String, nil]
|
|
def self.find(identifier, relation: User)
|
|
return if identifier.blank?
|
|
|
|
relation.where(id: identifier)
|
|
.or(relation.where(username: identifier))
|
|
.or(relation.where(email: identifier)).first
|
|
end
|
|
|
|
# @api public
|
|
# @param relation [ActiveRecord::Relation<User>]
|
|
# @param role [String, nil]
|
|
# @param search [String, nil]
|
|
# @param roles [Array<String>, nil]
|
|
# @param statuses [Array<String>, nil]
|
|
# @param ids [Array<Integer>, nil]
|
|
# @param organizations [Array<String>, nil]
|
|
# @param joining_start [String, nil]
|
|
# @param joining_end [String, nil]
|
|
# @param date_format [String]
|
|
# @param limit [Integer, nil]
|
|
def self.call(relation: User.registered,
|
|
role: nil,
|
|
search: nil,
|
|
roles: [],
|
|
organizations: [],
|
|
statuses: [],
|
|
ids: [],
|
|
joining_start: nil,
|
|
joining_end: nil,
|
|
date_format: "DD/MM/YYYY",
|
|
limit: nil)
|
|
# We are at an interstitial moment where we are exposing both the role and roles param. We
|
|
# need to favor one or the other.
|
|
if role.presence
|
|
relation = relation.with_role(role, :any)
|
|
elsif roles.presence || statuses.presence
|
|
# "statuses" are a subset of roles, so we can handle these filters together
|
|
relation = filter_roles(relation: relation, roles: [roles, statuses].compact.reduce([], :|))
|
|
end
|
|
|
|
if organizations.presence
|
|
relation = filter_organization_memberships(relation: relation, organizations: organizations)
|
|
end
|
|
|
|
if joining_start.presence || joining_end.presence
|
|
relation = filter_joining_date(relation: relation, joining_start: joining_start, joining_end: joining_end,
|
|
date_format: date_format)
|
|
end
|
|
|
|
relation = search_relation(relation, search) if search.presence
|
|
relation = filter_ids(relation, ids) if ids.presence
|
|
relation = relation.limit(limit.to_i) if limit.to_i.positive?
|
|
|
|
relation.distinct.order(created_at: :desc)
|
|
end
|
|
|
|
def self.search_relation(relation, search)
|
|
relation.where(SEARCH_CLAUSE, search: "%#{search.strip}%")
|
|
end
|
|
|
|
def self.filter_ids(relation, ids)
|
|
relation.where(id: ids)
|
|
end
|
|
|
|
def self.filter_joining_date(relation:, joining_start:, joining_end:, date_format:)
|
|
ui_formats_to_parse_format = {
|
|
"DD/MM/YYYY" => "%d/%m/%Y",
|
|
"MM/DD/YYYY" => "%m/%d/%Y"
|
|
}
|
|
parse_format = ui_formats_to_parse_format.fetch(date_format)
|
|
if joining_start.presence
|
|
relation = relation.where("registered_at >= ?", DateTime.strptime(joining_start, parse_format).beginning_of_day)
|
|
end
|
|
|
|
return relation unless joining_end.presence
|
|
|
|
relation.where("registered_at <= ?", DateTime.strptime(joining_end, parse_format).end_of_day)
|
|
end
|
|
|
|
def self.filter_organization_memberships(relation:, organizations:)
|
|
sub_query = OrganizationMembership.select(:user_id).where(organization_id: organizations)
|
|
relation.where(id: sub_query)
|
|
end
|
|
|
|
# Apply the "where" scope to the given relation for the given roles.
|
|
#
|
|
# @param relation [ActiveRecord::Relation<User>]
|
|
# @param roles [Array<String>]
|
|
#
|
|
# @note Why not use `relation.with_roles`; As implemented in Rolify's `.with_any_role` performs
|
|
# one User query per role passed. Given that we intend to use pagination, the one User
|
|
# query per role is inadequate.
|
|
#
|
|
# @see https://github.com/RolifyCommunity/rolify/blob/0c883f4173f409766338b9c6dfc64b0fc8ec8a52/lib/rolify/finders.rb#L26-L32
|
|
def self.filter_roles(relation:, roles:, role_map: Constants::Role::ALL_ROLES_LABELS_TO_WHERE_CLAUSE)
|
|
conditions = []
|
|
values = []
|
|
|
|
# Assemble the conditions and positional parameter values
|
|
roles.each do |role|
|
|
where_clause = role_map.fetch(role)
|
|
|
|
resource_type = where_clause.fetch(:resource_type, nil)
|
|
name = where_clause.fetch(:name)
|
|
|
|
condition = "(#{Role.table_name}.name = ? AND #{Role.table_name}.resource_id IS NULL"
|
|
values << name
|
|
|
|
# We need to use `IS NULL` instead of `= NULL` as those are different meanings.
|
|
if resource_type.nil?
|
|
condition += " AND #{Role.table_name}.resource_type IS NULL"
|
|
else
|
|
condition += " AND #{Role.table_name}.resource_type = ?"
|
|
values << resource_type
|
|
end
|
|
condition += ")"
|
|
conditions << condition
|
|
end
|
|
|
|
sub_query = User.select(:id).distinct.joins(:roles).where(%((#{conditions.join(') OR (')})), *values)
|
|
relation.where(id: sub_query)
|
|
end
|
|
private_class_method :filter_roles
|
|
end
|
|
end
|