Filter Group and Project members by Role
What does this MR do and why?
Filter Group and Project members by Role
.
Issue: #431397 (closed)
Screenshots or screen recordings
Before | After |
---|---|
Database
There are 2 scopes introduced in this MR:
- Member.with_static_role
- Member.with_member_role_id
Both query the member_role_id
FK-column, backed by the index_members_on_member_role_id
index.
The queries where they are used are as follows:
Member.with_static_role
Rails
GroupMembersFinder.new(group, user, params: { max_role: 'static-10' }).execute
Raw SQL
SELECT
"members".*
FROM
(
SELECT
DISTINCT ON (user_id, invite_email) *
FROM
"members"
WHERE
"members"."type" = 'GroupMember'
AND "members"."source_type" = 'Namespace'
AND "members"."requested_at" IS NULL
AND "members"."source_id" IN (
SELECT
"namespaces"."id"
FROM
(
(
SELECT
"namespaces"."id",
"namespaces"."name",
"namespaces"."path",
"namespaces"."owner_id",
"namespaces"."created_at",
"namespaces"."updated_at",
"namespaces"."type",
"namespaces"."description",
"namespaces"."avatar",
"namespaces"."membership_lock",
"namespaces"."share_with_group_lock",
"namespaces"."visibility_level",
"namespaces"."request_access_enabled",
"namespaces"."ldap_sync_status",
"namespaces"."ldap_sync_error",
"namespaces"."ldap_sync_last_update_at",
"namespaces"."ldap_sync_last_successful_update_at",
"namespaces"."ldap_sync_last_sync_at",
"namespaces"."description_html",
"namespaces"."lfs_enabled",
"namespaces"."parent_id",
"namespaces"."shared_runners_minutes_limit",
"namespaces"."repository_size_limit",
"namespaces"."require_two_factor_authentication",
"namespaces"."two_factor_grace_period",
"namespaces"."cached_markdown_version",
"namespaces"."project_creation_level",
"namespaces"."runners_token",
"namespaces"."file_template_project_id",
"namespaces"."saml_discovery_token",
"namespaces"."runners_token_encrypted",
"namespaces"."custom_project_templates_group_id",
"namespaces"."auto_devops_enabled",
"namespaces"."extra_shared_runners_minutes_limit",
"namespaces"."last_ci_minutes_notification_at",
"namespaces"."last_ci_minutes_usage_notification_level",
"namespaces"."subgroup_creation_level",
"namespaces"."max_pages_size",
"namespaces"."max_artifacts_size",
"namespaces"."mentions_disabled",
"namespaces"."default_branch_protection",
"namespaces"."max_personal_access_token_lifetime",
"namespaces"."push_rule_id",
"namespaces"."shared_runners_enabled",
"namespaces"."allow_descendants_override_disabled_shared_runners",
"namespaces"."traversal_ids",
"namespaces"."organization_id"
FROM
"namespaces"
WHERE
"namespaces"."type" = 'Group'
AND "namespaces"."id" = 9970
)
) namespaces
WHERE
"namespaces"."type" = 'Group'
)
ORDER BY
user_id,
invite_email,
CASE
WHEN source_id = 9970
and source_type = 'Namespace' THEN access_level + 1
ELSE access_level
END DESC,
expires_at DESC,
created_at ASC
) members
WHERE
"members"."type" = 'GroupMember'
AND "members"."access_level" = 10
AND "members"."member_role_id" IS NULL
Plan: https://console.postgres.ai/shared/8682af27-4755-452c-910a-15c50bac6d18
Member.with_custom_role
Rails
GroupMembersFinder.new(group, user, params: { max_role: 'custom-10' }).execute
Raw SQL
SELECT
"members".*
FROM
(
SELECT
DISTINCT ON (user_id, invite_email) *
FROM
"members"
WHERE
"members"."type" = 'GroupMember'
AND "members"."source_type" = 'Namespace'
AND "members"."requested_at" IS NULL
AND "members"."source_id" IN (
SELECT
"namespaces"."id"
FROM
(
(
SELECT
"namespaces"."id",
"namespaces"."name",
"namespaces"."path",
"namespaces"."owner_id",
"namespaces"."created_at",
"namespaces"."updated_at",
"namespaces"."type",
"namespaces"."description",
"namespaces"."avatar",
"namespaces"."membership_lock",
"namespaces"."share_with_group_lock",
"namespaces"."visibility_level",
"namespaces"."request_access_enabled",
"namespaces"."ldap_sync_status",
"namespaces"."ldap_sync_error",
"namespaces"."ldap_sync_last_update_at",
"namespaces"."ldap_sync_last_successful_update_at",
"namespaces"."ldap_sync_last_sync_at",
"namespaces"."description_html",
"namespaces"."lfs_enabled",
"namespaces"."parent_id",
"namespaces"."shared_runners_minutes_limit",
"namespaces"."repository_size_limit",
"namespaces"."require_two_factor_authentication",
"namespaces"."two_factor_grace_period",
"namespaces"."cached_markdown_version",
"namespaces"."project_creation_level",
"namespaces"."runners_token",
"namespaces"."file_template_project_id",
"namespaces"."saml_discovery_token",
"namespaces"."runners_token_encrypted",
"namespaces"."custom_project_templates_group_id",
"namespaces"."auto_devops_enabled",
"namespaces"."extra_shared_runners_minutes_limit",
"namespaces"."last_ci_minutes_notification_at",
"namespaces"."last_ci_minutes_usage_notification_level",
"namespaces"."subgroup_creation_level",
"namespaces"."max_pages_size",
"namespaces"."max_artifacts_size",
"namespaces"."mentions_disabled",
"namespaces"."default_branch_protection",
"namespaces"."max_personal_access_token_lifetime",
"namespaces"."push_rule_id",
"namespaces"."shared_runners_enabled",
"namespaces"."allow_descendants_override_disabled_shared_runners",
"namespaces"."traversal_ids",
"namespaces"."organization_id"
FROM
"namespaces"
WHERE
"namespaces"."type" = 'Group'
AND "namespaces"."id" = 9970
)
) namespaces
WHERE
"namespaces"."type" = 'Group'
)
ORDER BY
user_id,
invite_email,
CASE
WHEN source_id = 9970
and source_type = 'Namespace' THEN access_level + 1
ELSE access_level
END DESC,
expires_at DESC,
created_at ASC
) members
WHERE
"members"."type" = 'GroupMember'
AND "members"."member_role_id" = 10
Plan: https://console.postgres.ai/shared/5125433b-1a93-4cc0-8a1d-fd5563c8136f
How to set up and validate locally
- Visit any group or project member pages such as
http://127.0.0.1:3000/groups/flightjs/-/group_members
, click in the search bar, selectRole
and a role to filter by, and hit enter.
Edited by Alex Buijs