Skip to content

Commit 2d1e0a0

Browse files
committed
GitHub Importer - Find users based on their email address
The returned email by the GitHub API is the user's publicly visible email address (or null if the user has not specified a public email address in their profile)
1 parent 9fe863f commit 2d1e0a0

10 files changed

+191
-50
lines changed
+3-15
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
module Gitlab
22
module GithubImport
33
class BaseFormatter
4-
attr_reader :formatter, :project, :raw_data
4+
attr_reader :client, :formatter, :project, :raw_data
55

6-
def initialize(project, raw_data)
6+
def initialize(project, raw_data, client = nil)
77
@project = project
88
@raw_data = raw_data
9+
@client = client
910
@formatter = Gitlab::ImportFormatter.new
1011
end
1112

@@ -18,19 +19,6 @@ def create!
1819
def url
1920
raw_data.url || ''
2021
end
21-
22-
private
23-
24-
def gitlab_user_id(github_id)
25-
User.joins(:identities).
26-
find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s).
27-
try(:id)
28-
end
29-
30-
def gitlab_author_id
31-
return @gitlab_author_id if defined?(@gitlab_author_id)
32-
@gitlab_author_id = gitlab_user_id(raw_data.user.id)
33-
end
3422
end
3523
end
3624
end

lib/gitlab/github_import/comment_formatter.rb

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module Gitlab
22
module GithubImport
33
class CommentFormatter < BaseFormatter
4+
attr_writer :author_id
5+
46
def attributes
57
{
68
project: project,
@@ -17,11 +19,11 @@ def attributes
1719
private
1820

1921
def author
20-
raw_data.user.login
22+
@author ||= UserFormatter.new(client, raw_data.user)
2123
end
2224

2325
def author_id
24-
gitlab_author_id || project.creator_id
26+
author.gitlab_id || project.creator_id
2527
end
2628

2729
def body
@@ -52,10 +54,10 @@ def file_path
5254
end
5355

5456
def note
55-
if gitlab_author_id
57+
if author.gitlab_id
5658
body
5759
else
58-
formatter.author_line(author) + body
60+
formatter.author_line(author.login) + body
5961
end
6062
end
6163

lib/gitlab/github_import/importer.rb

+11-8
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def import_milestones
110110
def import_issues
111111
fetch_resources(:issues, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |issues|
112112
issues.each do |raw|
113-
gh_issue = IssueFormatter.new(project, raw)
113+
gh_issue = IssueFormatter.new(project, raw, client)
114114

115115
begin
116116
issuable =
@@ -131,7 +131,8 @@ def import_issues
131131
def import_pull_requests
132132
fetch_resources(:pull_requests, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |pull_requests|
133133
pull_requests.each do |raw|
134-
gh_pull_request = PullRequestFormatter.new(project, raw)
134+
gh_pull_request = PullRequestFormatter.new(project, raw, client)
135+
135136
next unless gh_pull_request.valid?
136137

137138
begin
@@ -209,14 +210,16 @@ def create_comments(comments)
209210
ActiveRecord::Base.no_touching do
210211
comments.each do |raw|
211212
begin
212-
comment = CommentFormatter.new(project, raw)
213+
comment = CommentFormatter.new(project, raw, client)
214+
213215
# GH does not return info about comment's parent, so we guess it by checking its URL!
214216
*_, parent, iid = URI(raw.html_url).path.split('/')
215-
if parent == 'issues'
216-
issuable = Issue.find_by(project_id: project.id, iid: iid)
217-
else
218-
issuable = MergeRequest.find_by(target_project_id: project.id, iid: iid)
219-
end
217+
218+
issuable = if parent == 'issues'
219+
Issue.find_by(project_id: project.id, iid: iid)
220+
else
221+
MergeRequest.find_by(target_project_id: project.id, iid: iid)
222+
end
220223

221224
next unless issuable
222225

lib/gitlab/github_import/issuable_formatter.rb

+17-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module Gitlab
22
module GithubImport
33
class IssuableFormatter < BaseFormatter
4+
attr_writer :assignee_id, :author_id
5+
46
def project_association
57
raise NotImplementedError
68
end
@@ -23,29 +25,35 @@ def assigned?
2325
raw_data.assignee.present?
2426
end
2527

26-
def assignee_id
28+
def author
29+
@author ||= UserFormatter.new(client, raw_data.user)
30+
end
31+
32+
def author_id
33+
@author_id ||= author.gitlab_id || project.creator_id
34+
end
35+
36+
def assignee
2737
if assigned?
28-
gitlab_user_id(raw_data.assignee.id)
38+
@assignee ||= UserFormatter.new(client, raw_data.assignee)
2939
end
3040
end
3141

32-
def author
33-
raw_data.user.login
34-
end
42+
def assignee_id
43+
return @assignee_id if defined?(@assignee_id)
3544

36-
def author_id
37-
gitlab_author_id || project.creator_id
45+
@assignee_id = assignee.try(:gitlab_id)
3846
end
3947

4048
def body
4149
raw_data.body || ""
4250
end
4351

4452
def description
45-
if gitlab_author_id
53+
if author.gitlab_id
4654
body
4755
else
48-
formatter.author_line(author) + body
56+
formatter.author_line(author.login) + body
4957
end
5058
end
5159

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
module Gitlab
2+
module GithubImport
3+
class UserFormatter
4+
attr_reader :client, :raw
5+
6+
delegate :id, :login, to: :raw, allow_nil: true
7+
8+
def initialize(client, raw)
9+
@client = client
10+
@raw = raw
11+
end
12+
13+
def gitlab_id
14+
return @gitlab_id if defined?(@gitlab_id)
15+
16+
@gitlab_id = find_by_external_uid || find_by_email
17+
end
18+
19+
private
20+
21+
def email
22+
@email ||= client.user(raw.login).try(:email)
23+
end
24+
25+
def find_by_email
26+
return nil unless email
27+
users = ::User.arel_table
28+
emails = ::Email.arel_table
29+
30+
left_join_emails = users.join(emails, Arel::Nodes::OuterJoin).on(
31+
users[:id].eq(emails[:user_id])
32+
).join_sources
33+
34+
User.select(:id)
35+
.joins(left_join_emails)
36+
.where(users[:email].eq(email).or(emails[:email].eq(email)))
37+
.first.try(:id)
38+
end
39+
40+
def find_by_external_uid
41+
return nil unless id
42+
43+
identities = ::Identity.arel_table
44+
45+
User.select(:id)
46+
.joins(:identities).where(identities[:provider].eq(:github)
47+
.and(identities[:extern_uid].eq(id))).
48+
first.try(:id)
49+
end
50+
end
51+
end
52+
end

spec/lib/gitlab/github_import/comment_formatter_spec.rb

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
require 'spec_helper'
22

33
describe Gitlab::GithubImport::CommentFormatter, lib: true do
4+
let(:client) { double }
45
let(:project) { create(:empty_project) }
5-
let(:octocat) { double(id: 123456, login: 'octocat') }
6+
let(:octocat) { double(id: 123456, login: 'octocat', email: '[email protected]') }
67
let(:created_at) { DateTime.strptime('2013-04-10T20:09:31Z') }
78
let(:updated_at) { DateTime.strptime('2014-03-03T18:58:10Z') }
89
let(:base) do
@@ -16,7 +17,11 @@
1617
}
1718
end
1819

19-
subject(:comment) { described_class.new(project, raw)}
20+
subject(:comment) { described_class.new(project, raw, client) }
21+
22+
before do
23+
allow(client).to receive(:user).and_return(octocat)
24+
end
2025

2126
describe '#attributes' do
2227
context 'when do not reference a portion of the diff' do
@@ -69,8 +74,15 @@
6974
context 'when author is a GitLab user' do
7075
let(:raw) { double(base.merge(user: octocat)) }
7176

72-
it 'returns GitLab user id as author_id' do
77+
it 'returns GitLab user id associated with GitHub id as author_id' do
7378
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
79+
80+
expect(comment.attributes.fetch(:author_id)).to eq gl_user.id
81+
end
82+
83+
it 'returns GitLab user id associated with GitHub email as author_id' do
84+
gl_user = create(:user, email: octocat.email)
85+
7486
expect(comment.attributes.fetch(:author_id)).to eq gl_user.id
7587
end
7688

spec/lib/gitlab/github_import/importer_spec.rb

+4-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound)
4545
allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error)
4646

47+
allow_any_instance_of(Octokit::Client).to receive(:user).and_return(octocat)
4748
allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label1, label2])
4849
allow_any_instance_of(Octokit::Client).to receive(:milestones).and_return([milestone, milestone])
4950
allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2])
@@ -53,7 +54,8 @@
5354
allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil }))
5455
allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2])
5556
end
56-
let(:octocat) { double(id: 123456, login: 'octocat') }
57+
58+
let(:octocat) { double(id: 123456, login: 'octocat', email: '[email protected]') }
5759
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
5860
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
5961
let(:label1) do
@@ -125,6 +127,7 @@
125127
)
126128
end
127129

130+
let!(:user) { create(:user, email: octocat.email) }
128131
let(:repository) { double(id: 1, fork: false) }
129132
let(:source_sha) { create(:commit, project: project).id }
130133
let(:source_branch) { double(ref: 'feature', repo: repository, sha: source_sha) }

spec/lib/gitlab/github_import/issue_formatter_spec.rb

+22-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
require 'spec_helper'
22

33
describe Gitlab::GithubImport::IssueFormatter, lib: true do
4+
let(:client) { double }
45
let!(:project) { create(:empty_project, namespace: create(:namespace, path: 'octocat')) }
5-
let(:octocat) { double(id: 123456, login: 'octocat') }
6+
let(:octocat) { double(id: 123456, login: 'octocat', email: '[email protected]') }
67
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
78
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
89

@@ -23,7 +24,11 @@
2324
}
2425
end
2526

26-
subject(:issue) { described_class.new(project, raw_data) }
27+
subject(:issue) { described_class.new(project, raw_data, client) }
28+
29+
before do
30+
allow(client).to receive(:user).and_return(octocat)
31+
end
2732

2833
shared_examples 'Gitlab::GithubImport::IssueFormatter#attributes' do
2934
context 'when issue is open' do
@@ -75,11 +80,17 @@
7580
expect(issue.attributes.fetch(:assignee_id)).to be_nil
7681
end
7782

78-
it 'returns GitLab user id as assignee_id when is a GitLab user' do
83+
it 'returns GitLab user id associated with GitHub id as assignee_id' do
7984
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
8085

8186
expect(issue.attributes.fetch(:assignee_id)).to eq gl_user.id
8287
end
88+
89+
it 'returns GitLab user id associated with GitHub email as assignee_id' do
90+
gl_user = create(:user, email: octocat.email)
91+
92+
expect(issue.attributes.fetch(:assignee_id)).to eq gl_user.id
93+
end
8394
end
8495

8596
context 'when it has a milestone' do
@@ -100,16 +111,22 @@
100111
context 'when author is a GitLab user' do
101112
let(:raw_data) { double(base_data.merge(user: octocat)) }
102113

103-
it 'returns project#creator_id as author_id when is not a GitLab user' do
114+
it 'returns project creator_id as author_id when is not a GitLab user' do
104115
expect(issue.attributes.fetch(:author_id)).to eq project.creator_id
105116
end
106117

107-
it 'returns GitLab user id as author_id when is a GitLab user' do
118+
it 'returns GitLab user id associated with GitHub id as author_id' do
108119
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
109120

110121
expect(issue.attributes.fetch(:author_id)).to eq gl_user.id
111122
end
112123

124+
it 'returns GitLab user id associated with GitHub email as author_id' do
125+
gl_user = create(:user, email: octocat.email)
126+
127+
expect(issue.attributes.fetch(:author_id)).to eq gl_user.id
128+
end
129+
113130
it 'returns description without created at tag line' do
114131
create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
115132

0 commit comments

Comments
 (0)