diff options
author | Stan Hu <stanhu@gmail.com> | 2018-06-25 13:06:10 -0700 |
---|---|---|
committer | Stan Hu <stanhu@gmail.com> | 2018-06-25 13:06:10 -0700 |
commit | ebd8e4333a263138abf2113dd315a97352851cbe (patch) | |
tree | 4e4975eab778d490c20c9252785761a1d78fae26 /lib | |
parent | 2bac2918b2d6f12d94f739f4b6865b9e9221c642 (diff) | |
download | gitlab-ce-ebd8e4333a263138abf2113dd315a97352851cbe.tar.gz |
WIP: Add support for Bitbucket Server imports
Diffstat (limited to 'lib')
-rw-r--r-- | lib/bitbucket_server/client.rb | 59 | ||||
-rw-r--r-- | lib/bitbucket_server/connection.rb | 35 | ||||
-rw-r--r-- | lib/bitbucket_server/page.rb | 34 | ||||
-rw-r--r-- | lib/bitbucket_server/paginator.rb | 36 | ||||
-rw-r--r-- | lib/bitbucket_server/representation/base.rb | 15 | ||||
-rw-r--r-- | lib/bitbucket_server/representation/comment.rb | 27 | ||||
-rw-r--r-- | lib/bitbucket_server/representation/issue.rb | 53 | ||||
-rw-r--r-- | lib/bitbucket_server/representation/pull_request.rb | 65 | ||||
-rw-r--r-- | lib/bitbucket_server/representation/pull_request_comment.rb | 39 | ||||
-rw-r--r-- | lib/bitbucket_server/representation/repo.rb | 71 | ||||
-rw-r--r-- | lib/bitbucket_server/representation/user.rb | 9 | ||||
-rw-r--r-- | lib/gitlab/import_sources.rb | 3 |
12 files changed, 445 insertions, 1 deletions
diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb new file mode 100644 index 00000000000..1f2f03790dd --- /dev/null +++ b/lib/bitbucket_server/client.rb @@ -0,0 +1,59 @@ +module BitbucketServer + class Client + attr_reader :connection + + def initialize(options = {}) + @connection = Connection.new(options) + end + + def issues(repo) + path = "/repositories/#{repo}/issues" + get_collection(path, :issue) + end + + def issue_comments(repo, issue_id) + path = "/repositories/#{repo}/issues/#{issue_id}/comments" + get_collection(path, :comment) + end + + def pull_requests(repo) + path = "/repositories/#{repo}/pullrequests?state=ALL" + get_collection(path, :pull_request) + end + + def pull_request_comments(repo, pull_request) + path = "/repositories/#{repo}/pullrequests/#{pull_request}/comments" + get_collection(path, :pull_request_comment) + end + + def pull_request_diff(repo, pull_request) + path = "/repositories/#{repo}/pullrequests/#{pull_request}/diff" + connection.get(path) + end + + def repo(project, repo_name) + parsed_response = connection.get("/projects/#{project}/repos/#{repo_name}") + # XXXX TODO Handle failure + BitbucketServer::Representation::Repo.new(parsed_response) + end + + def repos + path = "/repos" + get_collection(path, :repo) + end + + def user + @user ||= begin + parsed_response = connection.get('/user') + BitbucketServer::Representation::User.new(parsed_response) + end + end + + private + + def get_collection(path, type) + paginator = BitbucketServer::Paginator.new(connection, path, type) + BitbucketServer::Collection.new(paginator) + end + end +end diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb new file mode 100644 index 00000000000..64f12527a8d --- /dev/null +++ b/lib/bitbucket_server/connection.rb @@ -0,0 +1,35 @@ +module BitbucketServer + class Connection + DEFAULT_API_VERSION = '1.0'.freeze + + attr_reader :api_version, :base_uri, :username, :token + + def initialize(options = {}) + @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) + @base_uri = options[:base_uri] + @username = options[:username] + @token = options[:personal_access_token] + end + + def get(path, extra_query = {}) + auth = { username: username, password: token } + response = Gitlab::HTTP.get(build_url(path), + basic_auth: auth, + params: extra_query) + ## Handle failure + response.parsed_response + end + + private + + def build_url(path) + return path if path.starts_with?(root_url) + + "#{root_url}#{path}" + end + + def root_url + "#{base_uri}/rest/api/#{api_version}" + end + end +end diff --git a/lib/bitbucket_server/page.rb b/lib/bitbucket_server/page.rb new file mode 100644 index 00000000000..17be8cbb860 --- /dev/null +++ b/lib/bitbucket_server/page.rb @@ -0,0 +1,34 @@ +module BitbucketServer + class Page + attr_reader :attrs, :items + + def initialize(raw, type) + @attrs = parse_attrs(raw) + @items = parse_values(raw, representation_class(type)) + end + + def next? + !attrs.fetch(:isLastPage, true) + end + + def next + attrs.fetch(:nextPageStart) + end + + private + + def parse_attrs(raw) + raw.slice(*%w(size nextPageStart isLastPage)).symbolize_keys + end + + def parse_values(raw, bitbucket_rep_class) + return [] unless raw['values'] && raw['values'].is_a?(Array) + + bitbucket_rep_class.decorate(raw['values']) + end + + def representation_class(type) + BitbucketServer::Representation.const_get(type.to_s.camelize) + end + end +end diff --git a/lib/bitbucket_server/paginator.rb b/lib/bitbucket_server/paginator.rb new file mode 100644 index 00000000000..c995cf4c3bd --- /dev/null +++ b/lib/bitbucket_server/paginator.rb @@ -0,0 +1,36 @@ +module BitbucketServer + class Paginator + PAGE_LENGTH = 25 # The minimum length is 10 and the maximum is 100. + + def initialize(connection, url, type) + @connection = connection + @type = type + @url = url + @page = nil + end + + def items + raise StopIteration unless has_next_page? + + @page = fetch_next_page + @page.items + end + + private + + attr_reader :connection, :page, :url, :type + + def has_next_page? + page.nil? || page.next? + end + + def next_url + page.nil? ? url : page.next + end + + def fetch_next_page + parsed_response = connection.get(next_url, pagelen: PAGE_LENGTH, sort: :created_on) + Page.new(parsed_response, type) + end + end +end diff --git a/lib/bitbucket_server/representation/base.rb b/lib/bitbucket_server/representation/base.rb new file mode 100644 index 00000000000..11b32b70c4c --- /dev/null +++ b/lib/bitbucket_server/representation/base.rb @@ -0,0 +1,15 @@ +module BitbucketServer + module Representation + class Base + attr_reader :raw + + def initialize(raw) + @raw = raw + end + + def self.decorate(entries) + entries.map { |entry| new(entry)} + end + end + end +end diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb new file mode 100644 index 00000000000..4937aa9728f --- /dev/null +++ b/lib/bitbucket_server/representation/comment.rb @@ -0,0 +1,27 @@ +module Bitbucket + module Representation + class Comment < Representation::Base + def author + user['username'] + end + + def note + raw.fetch('content', {}).fetch('raw', nil) + end + + def created_at + raw['created_on'] + end + + def updated_at + raw['updated_on'] || raw['created_on'] + end + + private + + def user + raw.fetch('user', {}) + end + end + end +end diff --git a/lib/bitbucket_server/representation/issue.rb b/lib/bitbucket_server/representation/issue.rb new file mode 100644 index 00000000000..44bcbc250b3 --- /dev/null +++ b/lib/bitbucket_server/representation/issue.rb @@ -0,0 +1,53 @@ +module Bitbucket + module Representation + class Issue < Representation::Base + CLOSED_STATUS = %w(resolved invalid duplicate wontfix closed).freeze + + def iid + raw['id'] + end + + def kind + raw['kind'] + end + + def author + raw.dig('reporter', 'username') + end + + def description + raw.fetch('content', {}).fetch('raw', nil) + end + + def state + closed? ? 'closed' : 'opened' + end + + def title + raw['title'] + end + + def milestone + raw['milestone']['name'] if raw['milestone'].present? + end + + def created_at + raw['created_on'] + end + + def updated_at + raw['edited_on'] + end + + def to_s + iid + end + + private + + def closed? + CLOSED_STATUS.include?(raw['state']) + end + end + end +end diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb new file mode 100644 index 00000000000..3553f3adbc7 --- /dev/null +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -0,0 +1,65 @@ +module BitbucketServer + module Representation + class PullRequest < Representation::Base + def author + raw.fetch('author', {}).fetch('username', nil) + end + + def description + raw['description'] + end + + def iid + raw['id'] + end + + def state + if raw['state'] == 'MERGED' + 'merged' + elsif raw['state'] == 'DECLINED' + 'closed' + else + 'opened' + end + end + + def created_at + raw['created_on'] + end + + def updated_at + raw['updated_on'] + end + + def title + raw['title'] + end + + def source_branch_name + source_branch.fetch('branch', {}).fetch('name', nil) + end + + def source_branch_sha + source_branch.fetch('commit', {}).fetch('hash', nil) + end + + def target_branch_name + target_branch.fetch('branch', {}).fetch('name', nil) + end + + def target_branch_sha + target_branch.fetch('commit', {}).fetch('hash', nil) + end + + private + + def source_branch + raw['source'] + end + + def target_branch + raw['destination'] + end + end + end +end diff --git a/lib/bitbucket_server/representation/pull_request_comment.rb b/lib/bitbucket_server/representation/pull_request_comment.rb new file mode 100644 index 00000000000..c52acbc3ddc --- /dev/null +++ b/lib/bitbucket_server/representation/pull_request_comment.rb @@ -0,0 +1,39 @@ +module Bitbucket + module Representation + class PullRequestComment < Comment + def iid + raw['id'] + end + + def file_path + inline.fetch('path') + end + + def old_pos + inline.fetch('from') + end + + def new_pos + inline.fetch('to') + end + + def parent_id + raw.fetch('parent', {}).fetch('id', nil) + end + + def inline? + raw.key?('inline') + end + + def has_parent? + raw.key?('parent') + end + + private + + def inline + raw.fetch('inline', {}) + end + end + end +end diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb new file mode 100644 index 00000000000..f4bdb277d28 --- /dev/null +++ b/lib/bitbucket_server/representation/repo.rb @@ -0,0 +1,71 @@ +module BitbucketServer + module Representation + class Repo < Representation::Base + attr_reader :owner, :slug + + def initialize(raw) + super(raw) + end + + def owner + project['name'] + end + + def slug + raw['slug'] + end + + def clone_url(token = nil) + url = raw['links']['clone'].find { |link| link['name'].starts_with?('http') }.fetch('href') + + if token.present? + clone_url = URI.parse(url) + clone_url.user = "x-token-auth:#{token}" + clone_url.to_s + else + url + end + end + + def description + project['description'] + end + + def full_name + "#{owner}/#{name}" + end + + def issues_enabled? + true + end + + def name + raw['name'] + end + + def valid? + raw['scmId'] == 'git' + end + + def has_wiki? + false + end + + def visibility_level + if project['public'] + Gitlab::VisibilityLevel::PUBLIC + else + Gitlab::VisibilityLevel::PRIVATE + end + end + + def project + raw['project'] + end + + def to_s + full_name + end + end + end +end diff --git a/lib/bitbucket_server/representation/user.rb b/lib/bitbucket_server/representation/user.rb new file mode 100644 index 00000000000..174f3a55f2c --- /dev/null +++ b/lib/bitbucket_server/representation/user.rb @@ -0,0 +1,9 @@ +module BitbucketServer + module Representation + class User < Representation::Base + def username + raw['username'] + end + end + end +end diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb index 60d5fa4d29a..10289af6b25 100644 --- a/lib/gitlab/import_sources.rb +++ b/lib/gitlab/import_sources.rb @@ -10,7 +10,8 @@ module Gitlab # We exclude `bare_repository` here as it has no import class associated ImportTable = [ ImportSource.new('github', 'GitHub', Gitlab::GithubImport::ParallelImporter), - ImportSource.new('bitbucket', 'Bitbucket', Gitlab::BitbucketImport::Importer), + ImportSource.new('bitbucket', 'Bitbucket Cloud', Gitlab::BitbucketImport::Importer), + ImportSource.new('bitbucket_server', 'Bitbucket Server', Gitlab::BitbucketServerImport::Importer), ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer), ImportSource.new('google_code', 'Google Code', Gitlab::GoogleCodeImport::Importer), ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer), |