Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Bitbucket Server pull requests formatter #216

Merged
merged 2 commits into from
Apr 9, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/pronto.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
require 'pronto/config'

require 'pronto/clients/bitbucket_client'
require 'pronto/clients/bitbucket_server_client'

require 'pronto/git/repository'
require 'pronto/git/patches'
Expand All @@ -30,6 +31,7 @@
require 'pronto/github'
require 'pronto/gitlab'
require 'pronto/bitbucket'
require 'pronto/bitbucket_server'

require 'pronto/formatter/colorizable'
require 'pronto/formatter/base'
Expand All @@ -44,6 +46,7 @@
require 'pronto/formatter/gitlab_formatter'
require 'pronto/formatter/bitbucket_formatter'
require 'pronto/formatter/bitbucket_pull_request_formatter'
require 'pronto/formatter/bitbucket_server_pull_request_formatter'
require 'pronto/formatter/checkstyle_formatter'
require 'pronto/formatter/null_formatter'
require 'pronto/formatter/formatter'
Expand Down
35 changes: 35 additions & 0 deletions lib/pronto/bitbucket_server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module Pronto
class BitbucketServer < Bitbucket
def pull_comments(sha)
@comment_cache["#{pull_id}/#{sha}"] ||= begin
client.pull_comments(slug, pull_id).inject([]) do |comments, comment|
if comment['commentAnchor']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's a commentAnchor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It contains data about comment place (file path, line number, etc)

comments << Comment.new(sha,
comment['comment']['text'],
comment['commentAnchor']['path'],
comment['commentAnchor']['line'])
end
end
end
end

private

def client
@client ||= BitbucketServerClient.new(@config.bitbucket_username,
@config.bitbucket_password,
@config.bitbucket_api_endpoint)
end

def pull
@pull ||=
if env_pull_id
pull_requests.find { |pr| pr.id.to_i == env_pull_id.to_i }
elsif @repo.branch
pull_requests.find do |pr|
pr['fromRef']['displayId'] == @repo.branch
end
end
end
end
end
64 changes: 64 additions & 0 deletions lib/pronto/clients/bitbucket_server_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
class BitbucketServerClient
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could parts of this class be extracted to reduce duplication between BitbucketServerClient and BitbucketClient?

Copy link
Contributor Author

@andrey-skat andrey-skat Mar 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see too much duplication between these two 😔 They only have common interface

include HTTParty

def initialize(username, password, endpoint)
self.class.base_uri(endpoint)
self.class.basic_auth(username, password)
@headers = { 'Content-Type' => 'application/json' }
end

def pull_comments(slug, pr_id)
project_key, repository_key = slug.split('/')
url = "/projects/#{project_key}/repos/#{repository_key}/pull-requests/#{pr_id}/activities"
response = paged_request(url)
response.select { |activity| activity.action == 'COMMENTED' }
end

def pull_requests(slug)
project_key, repository_key = slug.split('/')
url = "/projects/#{project_key}/repos/#{repository_key}/pull-requests"
paged_request(url, state: 'OPEN')
end

def create_pull_comment(slug, pull_id, body, path, position)
project_key, repository_key = slug.split('/')
url = "/projects/#{project_key}/repos/#{repository_key}/pull-requests/#{pull_id}/comments"
post(url, body, path, position)
end

private

def openstruct(response)
response.map { |r| OpenStruct.new(r) }
end

def post(url, body, path, position)
body = {
text: body,
anchor: {
line: position,
lineType: 'ADDED',
path: path,
srcPath: path
}
}
self.class.post(url, body: body.to_json, headers: @headers)
end

def paged_request(url, query = {})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andrey-skat what's the reason for doing paged_request? Should we use it for BitbucketClient too?

Copy link
Contributor Author

@andrey-skat andrey-skat Mar 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bitbucket Server use paged API for get responses, so we must iterate over many pages.
As far as I know, Bitbucket cloud returns all comments at once.

Enumerator.new do |yielder|
next_page_start = 0
loop do
response = self.class.get(url, query.merge(start: next_page_start)).parsed_response
break if response['values'].nil?

response['values'].each do |item|
yielder << OpenStruct.new(item)
end

next_page_start = response['nextPageStart']
break unless next_page_start
end
end
end
end
1 change: 1 addition & 0 deletions lib/pronto/config_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class ConfigFile
'slug' => nil,
'username' => nil,
'password' => nil,
'api_endpoint' => nil,
'web_endpoint' => 'https://bitbucket.org/'
},
'text' => {
Expand Down
17 changes: 17 additions & 0 deletions lib/pronto/formatter/bitbucket_server_pull_request_formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Pronto
module Formatter
class BitbucketServerPullRequestFormatter < PullRequestFormatter
def client_module
BitbucketServer
end

def pretty_name
'BitBucket Server'
end

def line_number(message, _)
message.line.line.new_lineno if message.line
end
end
end
end
1 change: 1 addition & 0 deletions lib/pronto/formatter/formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def self.names
'gitlab' => GitlabFormatter,
'bitbucket' => BitbucketFormatter,
'bitbucket_pr' => BitbucketPullRequestFormatter,
'bitbucket_server_pr' => BitbucketServerPullRequestFormatter,
'json' => JsonFormatter,
'checkstyle' => CheckstyleFormatter,
'text' => TextFormatter,
Expand Down
48 changes: 48 additions & 0 deletions spec/pronto/bitbucket_server_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module Pronto
describe BitbucketServer do
let(:bitbucket) { described_class.new(repo) }

let(:repo) do
double(remote_urls: ['[email protected]:mmozuras/pronto.git'],
branch: nil)
end
let(:sha) { '61e4bef' }

describe '#pull_comments' do
subject { bitbucket.pull_comments(sha) }

let(:response) do
{
comment: {
text: 'text'
},
commentAnchor: {
path: '/path',
line: '1'
}
}
end

context 'three requests for same comments' do
specify do
BitbucketServerClient.any_instance
.should_receive(:pull_requests)
.once
.and_return([])

BitbucketServerClient.any_instance
.should_receive(:pull_comments)
.with('mmozuras/pronto', 10)
.once
.and_return([response])

ENV['PULL_REQUEST_ID'] = '10'

subject
subject
subject
end
end
end
end
end
20 changes: 20 additions & 0 deletions spec/pronto/clients/bitbucket_server_client_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Pronto
describe BitbucketServerClient do
let(:client) { described_class.new('username', 'password', 'endpoint') }

describe '#create_pull_comment' do
subject { client.create_pull_comment(slug, sha, body, path, position) }
let(:slug) { 'mmozuras/pronto' }
let(:sha) { '123' }
let(:body) { 'comment' }
let(:path) { 'path/to/file' }
let(:position) { 1 }

context 'success' do
before { BitbucketServerClient.stub(:post).and_return(response) }
let(:response) { double('Response', success?: true) }
its(:success?) { should be_truthy }
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module Pronto
module Formatter
describe BitbucketServerPullRequestFormatter do
let(:formatter) { described_class.new }

let(:repo) { Git::Repository.new('spec/fixtures/test.git') }

describe '#format' do
subject { formatter.format(messages, repo, patches) }
let(:messages) { [message, message] }
let(:message) { Message.new(patch.new_file_full_path, line, :info, '') }
let(:patch) { repo.show_commit('64dadfd').first }
let(:line) { patch.added_lines.first }
let(:patches) { repo.diff('64dadfd^') }

before do
ENV['PULL_REQUEST_ID'] = '10'
BitbucketServerClient.any_instance
.should_receive(:pull_requests)
.once
.and_return([])

BitbucketServerClient.any_instance
.should_receive(:pull_comments)
.once
.and_return([])
end

specify do
BitbucketServerClient.any_instance
.should_receive(:create_pull_comment)
.once

subject
end
end
end
end
end
3 changes: 2 additions & 1 deletion spec/pronto/formatter/formatter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ module Formatter
subject { Formatter.names }
it do
should =~ %w(github github_pr github_status gitlab bitbucket
bitbucket_pr json checkstyle text null)
bitbucket_pr json checkstyle text null
bitbucket_server_pr)
end
end
end
Expand Down