When moving a project, it's possible that some users who had
access to the project in old path can not access the project
in the new path.
Because `project_authorizations` records are updated asynchronously,
when we send the notification about moved project the list of project
team members contains old project members, we want to notify all these
members except the old users who can not access the new location.
In #37955,we see that the profile had a number of N+1 queries from repeated
access to `cross_reference_not_visible_for?`. This was optimized in previous
versions of GitLab by rendering all notes at once, counting the number of
visible references, and then using that number to check whether a system note
should be fully redacted.
There was also another N+1 query calling `ProjectTeam#member?`, which did not
take advantage of an optimization in prepare_notes_for_rendering that would
preload the maximum access level per project.
Closes#37955
this will remove the need make N queries (per-note) at the
cost of having to mark notes with an attribute
this opens up the possibility for other special roles for notes
When getting the max member access for a group of users, we stored the results
in RequestStore. However, this will only return results for project members, so
anyone who wasn't a member of the project would be checked once at the start,
and then once for each comment they made. These queries are generally quite
fast, but no query is faster!
Looking at the SQL log, we see useless queries such as:
```
D, [2017-03-22T03:25:00.726710 #2629] DEBUG -- : (235.9ms) SELECT MAX("project_authorizations"."access_level") AS maximum_access_level, "project_authorizations"."user_id" AS project_authorizations_user_id FROM "project_authorizations" WHERE "project_authorizations"."project_id" = 13083 AND 1=0 GROUP BY "project_authorizations"."user_id" [["project_id", 13083]]
```
This also updates _some_ specs to use these new methods, just to serve
as an example for others going forward, but by no means is this
exhaustive.
Original implementations at !5992 and !6012.
Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/20944
`ProjectTeam#find_member` doesn't take group links into account. It was
used in two places:
1. An admin view - it can stay here.
2. `ProjectTeam#member?`, which is often used to decide if a user has
access to view something.
This second part broke confidential issues viewing. `IssuesFinder` ends
up delegating to `Project#authorized_for_user?`, which does consider
group links, so users with access to the project via a group link could
see confidential issues on the index page. However, `IssuesPolicy` used
`ProjectTeam#member?`, so the same user couldn't view the issue when
going to it directly.
Changes include:
- Ensure Member.add_user is not called directly when not necessary
- New GroupMember.add_users_to_group to have the same abstraction level as for Project
- Refactor Member.add_user to take a source instead of an array of members
- Fix Rubocop offenses
- Always use Project#add_user instead of project.team.add_user
- Factorize users addition as members in Member.add_users_to_source
- Make access_level a keyword argument in GroupMember.add_users_to_group and ProjectMember.add_users_to_projects
- Destroy any requester before adding them as a member
- Improve the way we handle access requesters in Member.add_user
Instead of removing the requester and creating a new member,
we now simply accepts their access request. This way, they will
receive a "access request granted" email.
- Fix error that was previously silently ignored
- Stop raising when access level is invalid in Member, let Rails validation do their work
Signed-off-by: Rémy Coutable <remy@rymai.me>