Derive Current Organization from Path or Current User
What does this MR do and why?
We want to set Current.organization
based on these priorities (see #443829 (closed))
- URL path
- if that fails: Current User first organization
- If that fails: Default organization
This might change in the future but for Cells 1.0, this should work.
For mapping an URL to a current organization, I identified three cases
-
params[:namespace_id]
is set. We can use Routes to map this to an Organization -
params[:group_id]
is set. We can use Routes to map this to an Organization -
params[:id]
is set. We only use this if we are in GroupController
The Rack middleware will be removed. We will use a new Gitlab::Current::Organization
class that will be called from ApplicationController
. We still need to find out how we can do something similar for GraphQL and Rest API url's.
Database changes
Two scopes are added. Postgres.ai did not have snapshots at the time. My local dev database has data inserted with MASS_INSERT=1 and I also added a million fake organizations and organization_users to the database.
Organizations::Organization.with_namespace_path('gitlab/gitlab-org')
.
Explain output (Postgres.ai did not have a snapshot when I tried)
# EXPLAIN ANALYZE SELECT "organizations".* FROM "organizations" INNER JOIN "namespaces" ON "namespaces"."organization_id" = "organizations"."id" INNER JOIN "routes" "route" ON "route"."source_type" = 'Namespace' AND "route"."source_id" = "namespaces"."id" WHERE "route"."path" = 'gitlab/gitlab-org';
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=1.13..17.19 rows=1 width=90) (actual time=0.025..0.026 rows=0 loops=1)
-> Nested Loop (cost=0.98..17.02 rows=1 width=8) (actual time=0.025..0.025 rows=0 loops=1)
-> Index Scan using index_routes_on_path_text_pattern_ops on routes route (cost=0.55..8.57 rows=1 width=4) (actual time=0.025..0.025 rows=0 loops=1)
Index Cond: ((path)::text = 'gitlab/gitlab-org'::text)
Filter: ((source_type)::text = 'Namespace'::text)
-> Index Scan using namespaces_pkey on namespaces (cost=0.43..8.45 rows=1 width=12) (never executed)
Index Cond: (id = route.source_id)
-> Index Scan using organizations_pkey on organizations (cost=0.15..0.17 rows=1 width=90) (never executed)
Index Cond: (id = namespaces.organization_id)
Planning Time: 0.335 ms
Execution Time: 0.042 ms
So it is using indexes for all three tables. `
Organizations::Organization.with_user(user)
Explain output:
# EXPLAIN ANALYZE SELECT "organizations".* FROM "organizations" INNER JOIN "organization_users" ON "organization_users"."organization_id" = "organizations"."id" INNER JOIN "users" ON "users"."id" = "organization_users"."user_id" WHERE "users"."id" = 1 ORDER BY organization_users.id asc;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort (cost=1128.89..1129.13 rows=98 width=100) (actual time=0.057..0.058 rows=1 loops=1)
Sort Key: organization_users.id
Sort Method: quicksort Memory: 25kB
-> Nested Loop (cost=9.90..1125.65 rows=98 width=100) (actual time=0.054..0.055 rows=1 loops=1)
-> Index Only Scan using users_pkey on users (cost=0.42..4.44 rows=1 width=4) (actual time=0.031..0.032 rows=1 loops=1)
Index Cond: (id = 1)
Heap Fetches: 0
-> Nested Loop (cost=9.47..1120.22 rows=98 width=108) (actual time=0.021..0.022 rows=1 loops=1)
-> Bitmap Heap Scan on organization_users (cost=9.05..296.86 rows=98 width=24) (actual time=0.004..0.005 rows=1 loops=1)
Recheck Cond: (user_id = 1)
Heap Blocks: exact=1
-> Bitmap Index Scan on index_organization_users_on_user_id (cost=0.00..9.02 rows=98 width=0) (actual time=0.003..0.003 rows=1 loops=1)
Index Cond: (user_id = 1)
-> Index Scan using organizations_pkey on organizations (cost=0.42..8.40 rows=1 width=92) (actual time=0.016..0.016 rows=1 loops=1)
Index Cond: (id = organization_users.organization_id)
Planning Time: 0.299 ms
Execution Time: 0.074 ms
All joins are using indexes
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Testing
# Create two new orgs
snake = Organizations::Organization.create(name: "Snake Oil", path: 'snake-oil')
gitlab = Organizations::Organization.create(name: "GitLab", path: 'gitlab')
# Make root user member of both
root = User.find_by_username('root')
snake.users << root
gitlab.users << root
# Assign Org snake to Groups
twitter = Group.find_by_path('twitter')
twitter.update(organization: snake)
glorg = Group.find_by_path('gitlab-org')
glorg.update(organization: gitlab)
We do not support rendering Current Organization name yet, so I modified this haml file:
# app/views/groups/_home_panel.html.haml
= @group.name
= Current.organization.name
Now go to /twitter/
url, the group name will now be Twitter Snake Oil (= Current Organization)
/gitlab-org/
url: the group name will have Gitlab
Other groups will have 'Default'
Related to #443829 (closed)