-
Notifications
You must be signed in to change notification settings - Fork 245
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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'] | ||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
class BitbucketServerClient | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could parts of this class be extracted to reduce duplication between There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 = {}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @andrey-skat what's the reason for doing There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
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 |
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 |
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 |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's a
commentAnchor
?There was a problem hiding this comment.
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)