Add with_developer_access scope to Group association

What does this MR do and why?

Add with_developer_access scope to Group association

We need to be able to filter these groups by their access level for code owners.

Related to Acceptable CODEOWNERS Groups Should Inherit fro... (#288851 - closed)

This will allows us to load all groups that have been shared with the project's group or any of the group's ancestors while filtering out any guest, planner, and reporter groups which are unable to approve MRs.

References

Acceptable CODEOWNERS Groups Should Inherit fro... (#288851 - closed)

Validate locally

  1. Create a top-level group Group1
  2. Create a sub group Group1/Group2
  3. Create an external group Group3
  4. Create an external group Group4
  5. Within Group1, go to members, then invite Group3 with max role of Developer and Group4 with max role of Reporter
  6. Go to the console and find Group.find_by_full_path('Group1/Group2')
  7. Check that the with_developer_access level returns Group3 but not Group4.

You could also test the other max role settings too if you would like to be comprehensive.

Database stats

explain SELECT "namespaces"."id", "namespaces"."name", "namespaces"."path", "namespaces"."owner_id", "namespaces"."created_at", "namespaces"."updated_at", "namespaces"."type", "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"."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"."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" INNER JOIN "group_group_links" ON "namespaces"."id" = "group_group_links"."shared_with_group_id" WHERE "namespaces"."type" = 'Group' AND "group_group_links"."shared_group_id" IN (SELECT "namespaces"."id" FROM "namespaces" WHERE "namespaces"."type" = 'Group' AND "namespaces"."id" = 9970) AND "group_group_links"."group_access" IN (30, 40, 50)
 Nested Loop Semi Join  (cost=1.56..11.77 rows=1 width=370) (actual time=0.067..0.068 rows=1 loops=1)
   Buffers: shared hit=20
   I/O Timings: read=0.000 write=0.000
   ->  Nested Loop  (cost=0.99..8.17 rows=1 width=378) (actual time=0.051..0.053 rows=1 loops=1)
         Buffers: shared hit=15
         I/O Timings: read=0.000 write=0.000
         ->  Index Scan using index_group_group_links_on_shared_group_and_shared_with_group on public.group_group_links  (cost=0.42..4.58 rows=1 width=16) (actual time=0.030..0.030 rows=1 loops=1)
               Index Cond: (group_group_links.shared_group_id = 9970)
               Filter: (group_group_links.group_access = ANY ('{30,40,50}'::integer[]))
               Rows Removed by Filter: 0
               Buffers: shared hit=7
               I/O Timings: read=0.000 write=0.000
         ->  Index Scan using index_namespaces_on_type_and_id on public.namespaces  (cost=0.57..3.59 rows=1 width=370) (actual time=0.019..0.019 rows=1 loops=1)
               Index Cond: (((namespaces.type)::text = 'Group'::text) AND (namespaces.id = group_group_links.shared_with_group_id))
               Buffers: shared hit=8
               I/O Timings: read=0.000 write=0.000
   ->  Index Only Scan using index_namespaces_on_type_and_id on public.namespaces namespaces_1  (cost=0.57..3.59 rows=1 width=4) (actual time=0.014..0.014 rows=1 loops=1)
         Index Cond: ((namespaces_1.type = 'Group'::text) AND (namespaces_1.id = 9970))
         Heap Fetches: 0
         Buffers: shared hit=5
         I/O Timings: read=0.000 write=0.000
Settings: work_mem = '100MB', random_page_cost = '1.5', seq_page_cost = '4', effective_cache_size = '472585MB', jit = 'off'
Edited by Joe Woodward

Merge request reports

Loading