Commit 376dafb0 authored by Stan Hu's avatar Stan Hu

Merge branch '218609-bitbucket-server-importer-by-slug' into 'master'

Add user mapping by username to Bitbucket Server importer

Closes #218609

See merge request gitlab-org/gitlab!36885
parents d93fc08b efb28aa4
---
title: Add user mapping by username when importing projects for Bitbucket Server importer
merge_request: 36885
author:
type: added
---
name: bitbucket_server_user_mapping_by_username
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36885
rollout_issue_url:
group: group::import
type: development
default_enabled: false
......@@ -62,6 +62,25 @@ The importer will create any new namespaces (groups) if they don't exist or in
the case the namespace is taken, the repository will be imported under the user's
namespace that started the import process.
#### User assignment by username
Alternatively, user assignment by username is available behind a `bitbucket_server_user_mapping_by_username` feature flag.
The importer will try to find a user in the GitLab user database using author's `username` or `slug` or `displayName`.
Falls back to author's `email` if user is not found by username.
Similarly to user assignment by email, if no such user is available, the project creator is set as the author.
To enable or disable user assignment by username:
Start a [Rails console](../../../administration/troubleshooting/debug.md#starting-a-rails-console-session).
```ruby
# Enable
Feature.enable(:bitbucket_server_user_mapping_by_username)
# Disable
Feature.disable(:bitbucket_server_user_mapping_by_username)
```
## Importing your Bitbucket repositories
1. Sign in to GitLab and go to your dashboard.
......
......@@ -38,7 +38,9 @@ module BitbucketServer
end
def author_username
author['displayName']
author['username'] ||
author['slug'] ||
author['displayName']
end
def author_email
......
......@@ -11,6 +11,12 @@ module BitbucketServer
raw.dig('author', 'user', 'emailAddress')
end
def author_username
raw.dig('author', 'user', 'username') ||
raw.dig('author', 'user', 'slug') ||
raw.dig('author', 'user', 'displayName')
end
def description
raw['description']
end
......
......@@ -61,17 +61,18 @@ module Gitlab
}.to_json)
end
def gitlab_user_id(email)
find_user_id(email) || project.creator_id
end
def find_user_id(by:, value:)
return unless value
def find_user_id(email)
return unless email
return users[value] if users.key?(value)
return users[email] if users.key?(email)
user = if by == :email
User.find_by_any_email(value, confirmed: true)
else
User.find_by_username(value)
end
user = User.find_by_any_email(email, confirmed: true)
users[email] = user&.id
users[value] = user&.id
user&.id
end
......@@ -197,9 +198,8 @@ module Gitlab
log_info(stage: 'import_bitbucket_pull_requests', message: 'starting', iid: pull_request.iid)
description = ''
description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email)
description += author_line(pull_request)
description += pull_request.description if pull_request.description
author_id = gitlab_user_id(pull_request.author_email)
attributes = {
iid: pull_request.iid,
......@@ -212,7 +212,7 @@ module Gitlab
target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name),
target_branch_sha: pull_request.target_branch_sha,
state_id: MergeRequest.available_states[pull_request.state],
author_id: author_id,
author_id: author_id(pull_request),
created_at: pull_request.created_at,
updated_at: pull_request.updated_at
}
......@@ -254,7 +254,7 @@ module Gitlab
committer = merge_event.committer_email
user_id = gitlab_user_id(committer)
user_id = find_user_id(by: :email, value: committer) || project.creator_id
timestamp = merge_event.merge_timestamp
merge_request.update({ merge_commit_sha: merge_event.merge_commit })
metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request)
......@@ -353,7 +353,7 @@ module Gitlab
end
def pull_request_comment_attributes(comment)
author = find_user_id(comment.author_email)
author = uid(comment)
note = ''
unless author
......@@ -397,6 +397,23 @@ module Gitlab
def metrics
@metrics ||= Gitlab::Import::Metrics.new(:bitbucket_server_importer, @project)
end
def author_line(rep_object)
return '' if uid(rep_object)
@formatter.author_line(rep_object.author)
end
def author_id(rep_object)
uid(rep_object) || project.creator_id
end
def uid(rep_object)
find_user_id(by: :email, value: rep_object.author_email) unless Feature.enabled?(:bitbucket_server_user_mapping_by_username)
find_user_id(by: :username, value: rep_object.author_username) ||
find_user_id(by: :email, value: rep_object.author_email)
end
end
end
end
......@@ -20,7 +20,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [
......@@ -38,7 +39,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [
......@@ -56,7 +58,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [],
......@@ -85,7 +88,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"createdDate": 1530164016725,
......@@ -115,7 +119,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"createdDate": 1530164026000,
......@@ -147,7 +152,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [],
......@@ -194,7 +200,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [],
......@@ -363,7 +370,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
}
},
......@@ -383,7 +391,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [],
......@@ -543,7 +552,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
}
},
......@@ -563,7 +573,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [
......@@ -581,7 +592,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [
......@@ -599,7 +611,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [],
......@@ -789,7 +802,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
}
},
......@@ -809,7 +823,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [],
......@@ -843,7 +858,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
}
},
......@@ -863,7 +879,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"authorTimestamp": 1529727872000,
......@@ -880,7 +897,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"committerTimestamp": 1529727872000,
......@@ -951,7 +969,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
}
},
......@@ -971,7 +990,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [
......@@ -989,7 +1009,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [],
......@@ -1038,7 +1059,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
}
},
......@@ -1058,7 +1080,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
},
"comments": [],
......@@ -1092,7 +1115,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
}
},
......@@ -1113,7 +1137,8 @@
]
},
"name": "root",
"slug": "root",
"slug": "slug",
"username": "username",
"type": "NORMAL"
}
}
......
......@@ -5,8 +5,9 @@
"status":"UNAPPROVED",
"user":{
"active":true,
"displayName":"root",
"displayName":"displayName",
"emailAddress":"joe.montana@49ers.com",
"username": "username",
"id":1,
"links":{
"self":[
......@@ -16,7 +17,7 @@
]
},
"name":"root",
"slug":"root",
"slug":"slug",
"type":"NORMAL"
}
},
......
......@@ -13,7 +13,30 @@ RSpec.describe BitbucketServer::Representation::Comment do
end
describe '#author_username' do
it { expect(subject.author_username).to eq('root' ) }
it 'returns username' do
expect(subject.author_username).to eq('username')
end
context 'when username is absent' do
before do
comment['comment']['author'].delete('username')
end
it 'returns slug' do
expect(subject.author_username).to eq('slug')
end
end
context 'when slug and username are absent' do
before do
comment['comment']['author'].delete('username')
comment['comment']['author'].delete('slug')
end
it 'returns displayName' do
expect(subject.author_username).to eq('root')
end
end
end
describe '#author_email' do
......
......@@ -15,6 +15,33 @@ RSpec.describe BitbucketServer::Representation::PullRequest do
it { expect(subject.author_email).to eq('joe.montana@49ers.com') }
end
describe '#author_username' do
it 'returns username' do
expect(subject.author_username).to eq('username')
end
context 'when username is absent' do
before do
sample_data['author']['user'].delete('username')
end
it 'returns slug' do
expect(subject.author_username).to eq('slug')
end
end
context 'when slug and username are absent' do
before do
sample_data['author']['user'].delete('username')
sample_data['author']['user'].delete('slug')
end
it 'returns displayName' do
expect(subject.author_username).to eq('displayName')
end
end
end
describe '#description' do
it { expect(subject.description).to eq('Test') }
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment