Class: Gitlab::GithubImport::UserFinder

Inherits:
Object
  • Object
show all
Defined in:
lib/gitlab/github_import/user_finder.rb

Overview

Class that can be used for finding a GitLab user ID based on a GitHub user ID or username.

Any found user IDs are cached in Redis to reduce the number of SQL queries executed over time. Valid keys are refreshed upon access so frequently used keys stick around.

Lookups are cached even if no ID was found to remove the need for querying the database when most queries are not going to return results anyway.

Constant Summary collapse

ID_CACHE_KEY =

The base cache key to use for caching user IDs for a given GitHub user ID.

'github-import/user-finder/user-id/%s'
ID_FOR_EMAIL_CACHE_KEY =

The base cache key to use for caching user IDs for a given GitHub email address.

'github-import/user-finder/id-for-email/%s'
EMAIL_FOR_USERNAME_CACHE_KEY =

The base cache key to use for caching the Email addresses of GitHub usernames.

'github-import/user-finder/email-for-username/%s'
USERNAME_ETAG_CACHE_KEY =

The base cache key to use for caching the user ETAG response headers

'github-import/user-finder/user-etag/%s'
EMAIL_FETCHED_FOR_PROJECT_CACHE_KEY =

The base cache key to store whether an email has been fetched for a project

'github-import/user-finder/%{project}/email-fetched/%{username}'
EMAIL_API_CALL_LOGGING_MESSAGE =
{
  true => 'Fetching email from GitHub with ETAG header',
  false => 'Fetching email from GitHub'
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project, client) ⇒ UserFinder

project - An instance of ‘Project` client - An instance of `Gitlab::GithubImport::Client`



44
45
46
47
# File 'lib/gitlab/github_import/user_finder.rb', line 44

def initialize(project, client)
  @project = project
  @client = client
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



15
16
17
# File 'lib/gitlab/github_import/user_finder.rb', line 15

def client
  @client
end

#projectObject (readonly)

Returns the value of attribute project.



15
16
17
# File 'lib/gitlab/github_import/user_finder.rb', line 15

def project
  @project
end

Instance Method Details

#assignee_id_for(issuable) ⇒ Object

Returns the GitLab user ID of an issuable’s assignee.



78
79
80
# File 'lib/gitlab/github_import/user_finder.rb', line 78

def assignee_id_for(issuable)
  user_id_for(issuable[:assignee]) if issuable[:assignee]
end

#author_id_for(object, author_key: :author) ⇒ Object

Returns the GitLab user ID of an object’s author.

If the object has no author ID we’ll use the ID of the GitLab ghost user. object - An instance of ‘Hash` or a `Github::Representer`



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/gitlab/github_import/user_finder.rb', line 54

def author_id_for(object, author_key: :author)
   = case author_key
              when :actor
                object[:actor]
              when :assignee
                object[:assignee]
              when :requested_reviewer
                object[:requested_reviewer]
              when :review_requester
                object[:review_requester]
              else
                object ? object[:author] : nil
              end

  id =  ? user_id_for() : GithubImport.ghost_user_id

  if id
    [id, true]
  else
    [project.creator_id, false]
  end
end

#cached_id_for_github_email(email) ⇒ Object



163
164
165
# File 'lib/gitlab/github_import/user_finder.rb', line 163

def cached_id_for_github_email(email)
  read_id_from_cache(ID_FOR_EMAIL_CACHE_KEY % email)
end

#cached_id_for_github_id(id) ⇒ Object



159
160
161
# File 'lib/gitlab/github_import/user_finder.rb', line 159

def cached_id_for_github_id(id)
  read_id_from_cache(ID_CACHE_KEY % id)
end

#email_for_github_username(username) ⇒ String, Nil

Parameters:

  • username (String)

    The username of the GitHub user.

Returns:

  • (String)

    If public email is found

  • (Nil)

    If public email or username does not exist



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/gitlab/github_import/user_finder.rb', line 132

def email_for_github_username(username)
  email = read_email_from_cache(username)

  if email.blank? && !email_fetched_for_project?(username)
    # If an ETAG is available, make an API call with the ETAG.
    # Only make a rate-limited API call if the ETAG is not available and the email is nil.
    etag = read_etag_from_cache(username)
    email = fetch_email_from_github(username, etag: etag) || email

    cache_email!(username, email)
    cache_etag!(username) if email.blank? && etag.nil?

    # If a non-blank email is cached, we don't need the ETAG or project check caches.
    # Otherwise, indicate that the project has been checked.
    if email.present?
      clear_caches!(username)
    else
      set_project_as_checked!(username)
    end
  end

  email.presence
rescue ::Octokit::NotFound
  cache_email!(username, '')
  nil
end

#find(id, username) ⇒ Object

Returns the GitLab ID for the given GitHub ID or username.

id - The ID of the GitHub user. username - The username of the GitHub user.



93
94
95
96
97
98
99
100
101
102
103
# File 'lib/gitlab/github_import/user_finder.rb', line 93

def find(id, username)
  email = email_for_github_username(username)
  cached, found_id = find_from_cache(id, email)

  return found_id if found_id

  # We only want to query the database if necessary. If previous lookups
  # didn't yield a user ID we won't query the database again until the
  # keys expire.
  find_id_from_database(id, email) unless cached
end

#find_from_cache(id, email = nil) ⇒ Object

Finds a user ID from the cache for a given GitHub ID or Email.



106
107
108
109
110
111
112
113
114
115
# File 'lib/gitlab/github_import/user_finder.rb', line 106

def find_from_cache(id, email = nil)
  id_exists, id_for_github_id = cached_id_for_github_id(id)

  return [id_exists, id_for_github_id] if id_for_github_id

  # Just in case no Email address could be retrieved (for whatever reason)
  return [false] unless email

  cached_id_for_github_email(email)
end

#find_id_from_database(id, email) ⇒ Object

Finds a GitLab user ID from the database for a given GitHub user ID or Email.



119
120
121
# File 'lib/gitlab/github_import/user_finder.rb', line 119

def find_id_from_database(id, email)
  id_for_github_id(id) || id_for_github_email(email)
end

#id_for_github_email(email) ⇒ Object

Queries and caches the GitLab user ID for a GitHub email, if one was found.



185
186
187
188
189
# File 'lib/gitlab/github_import/user_finder.rb', line 185

def id_for_github_email(email)
  gitlab_id = query_id_for_github_email(email) || nil

  Gitlab::Cache::Import::Caching.write(ID_FOR_EMAIL_CACHE_KEY % email, gitlab_id)
end

#id_for_github_id(id) ⇒ Object

If importing from github.com, queries and caches the GitLab user ID for a GitHub user ID, if one was found.

When importing from Github Enterprise, do not query user by Github ID since we only have users’ Github ID from github.com.



172
173
174
175
176
177
178
179
180
181
# File 'lib/gitlab/github_import/user_finder.rb', line 172

def id_for_github_id(id)
  gitlab_id =
    if project.github_enterprise_import?
      nil
    else
      query_id_for_github_id(id)
    end

  Gitlab::Cache::Import::Caching.write(ID_CACHE_KEY % id, gitlab_id)
end

#query_id_for_github_email(email) ⇒ Object

rubocop: disable CodeReuse/ActiveRecord



198
199
200
# File 'lib/gitlab/github_import/user_finder.rb', line 198

def query_id_for_github_email(email)
  User.by_any_email(email).pick(:id)
end

#query_id_for_github_id(id) ⇒ Object

rubocop: disable CodeReuse/ActiveRecord



192
193
194
# File 'lib/gitlab/github_import/user_finder.rb', line 192

def query_id_for_github_id(id)
  User.by_provider_and_extern_uid(:github, id).select(:id).first&.id
end

#read_id_from_cache(key) ⇒ Object

Reads an ID from the cache.

The return value is an Array with two values:

  1. A boolean indicating if the key was present or not.

  2. The ID as an Integer, or nil in case no ID could be found.



209
210
211
212
213
214
215
216
217
# File 'lib/gitlab/github_import/user_finder.rb', line 209

def read_id_from_cache(key)
  value = Gitlab::Cache::Import::Caching.read(key)
  exists = !value.nil?
  number = value.to_i

  # The cache key may be empty to indicate a previously looked up user for
  # which we couldn't find an ID.
  [exists, number > 0 ? number : nil]
end

#user_id_for(user) ⇒ Object

Returns the GitLab user ID for a GitHub user.

user - An instance of ‘Gitlab::GithubImport::Representation::User` or `Hash`.



85
86
87
# File 'lib/gitlab/github_import/user_finder.rb', line 85

def user_id_for(user)
  find(user[:id], user[:login]) if user.present?
end