Permalink
Browse files

Add a service hook for Backlog

  • Loading branch information...
1 parent 4704c74 commit b3f3a0d6c284cebce23a6ad4081e4e0c97dd8628 @dragon3 dragon3 committed Sep 10, 2012
Showing with 181 additions and 0 deletions.
  1. +14 −0 docs/backlog
  2. +113 −0 services/backlog.rb
  3. +54 −0 test/backlog_test.rb
View
@@ -0,0 +1,14 @@
+Backlog
+===========
+
+Backlog is a project management, hosting service.
+http://backlogtool.com, http:/www.backlog.jp (Japanese)
+
+Install Notes
+-------------
+
+1. **api_url** is a API URL. e.g. if the Space ID is example, then the api_url will be https://example.backlog.jp/XML-RPC or https://example.backlogtoo.com/XML-RPC
+
+2. **user_id** and **password** - user_id and password of a Backlog user that can post comment and update issue.
+
+You can specify issue keys (e.g. DORA-1) and status keywords (#fixed, #closed) to change issue status (Resolved, Closed).
View
@@ -0,0 +1,113 @@
+class Service::Backlog < Service
+ string :api_url, :user_id
+ password :password
+ white_list :space_id, :user_id
+
+ def receive_push
+ if data['api_url'].to_s.empty?
+ raise_config_error "Backlog API URL not set"
+ end
+ if data['user_id'].to_s.empty?
+ raise_config_error "user_id not set"
+ end
+ if data['password'].to_s.empty?
+ raise_config_error "password not set"
+ end
+
+ repository = payload['repository']['url'].to_s
+ commits = payload['commits'].collect{|c| Commit.new(c)}
+ issue_commits = sort_commits(commits)
+ issue_commits.sort.map do | issue, commits |
+ post(issue, repository, commits, branch.to_s)
+ end
+
+ end
+
+ def branch
+ return @branch if defined?(@branch)
+
+ matches = payload['ref'].match(/^refs\/heads\/(.*)$/)
+ @branch = matches ? matches[1] : nil
+ end
+
+ attr_writer :xmlrpc_client
+ def xmlrpc_client
+ @xmlrpc_client ||= begin
+ uri = data['api_url'].to_s.sub('https://', "https://#{data['user_id'].to_s}:#{data['password'].to_s}@")
+ client = XMLRPC::Client.new2(uri)
+ # call for auth check
+ client.call('backlog.getProjects')
+ client
+ rescue XMLRPC::FaultException
+ raise_config_error "Invalid login details"
+ rescue SocketError, RuntimeError
+ raise_config_error "Invalid server url"
+ end
+ end
+
+ def sort_commits(commits)
+ issue_commits = Hash.new{|k,v| k[v] = []}
+ commits.each do |commit|
+ commit.issue.each do |issue|
+ issue_commits[issue] << commit
+ end
+ end
+ return issue_commits
+ end
+
+ def post(issue, repository, commits, branch_name)
+ if commits.length == 0
+ return
+ end
+
+ branch_str = branch_name.empty? ? "" : "#{branch_name} at "
+ message = "pushed to #{branch_str}#{repository}\n\n"
+
+ commits.each do |commit|
+ comment = "#{message}#{commit.comment}"
+ begin
+ if commit.status
+ xmlrpc_client.call('backlog.switchStatus', {'key' => issue, 'statusId' => commit.status, 'comment' => comment})
+ else
+ xmlrpc_client.call('backlog.addComment', {'key' => issue, 'content' => comment})
+ end
+ rescue XMLRPC::FaultException
+ raise_config_error "failed post"
+ rescue RuntimeError
+ raise_config_error "failed post"
+ end
+ end
+ end
+
+ class Commit
+ attr_reader :status, :issue, :url, :id
+
+ def initialize(commit_hash)
+ @id = commit_hash['id'].to_s
+ @url = commit_hash['url'].to_s
+ @message = commit_hash['message'].to_s
+ @status = nil
+ @issue = []
+
+ re_issue_key = /(?:\[\[)?(([A-Z0-9]+(?:_[A-Z0-9]+)*)-([1-9][0-9]*))(?:\]\])?/
+ temp = @message
+ while temp =~ re_issue_key
+ issue << $1
+ temp.sub!($1, '')
+ end
+
+ re_status = /(?:^|\s+?)(#fixes|#fixed|#fix|#closes|#closed|#close)(?:\s+?|$)/
+ while @message =~ re_status
+ switch = $1
+ @message.sub!(switch, '')
+ @status = (switch =~ /fix/) ? 3 : 4
+ end
+ end
+
+ def comment()
+ output = "#{@url}\n"
+ output += @message.strip
+ return output
+ end
+ end
+end
View
@@ -0,0 +1,54 @@
+require File.expand_path('../helper', __FILE__)
+
+class BacklogTest < Service::TestCase
+ def setup
+ @server = FakeXMLRPC.new()
+ end
+
+ def test_push
+ modified_payload = modify_payload(payload)
+ svc = service({'api_url' => 'https://demo.backlog.jp/XML-RPC', 'user_id' => 'someone', 'password' => '12345'}, modified_payload)
+ svc.xmlrpc_client = @server
+ svc.receive_push
+
+ assert @server.commented.length == 1
+ assert @server.commented[0]['content'].include? '06f63b43050935962f84fe54473a7c5de7977325'
+ assert @server.switched.length == 2
+ assert @server.switched[0]['comment'].include? '5057e76a11abd02e83b7d3d3171c4b68d9c88480'
+ assert @server.switched[0]['status'] == 3
+ assert @server.switched[1]['comment'].include? 'a47fd41f3aa4610ea527dcc1669dfdb9c15c5425'
+ assert @server.switched[1]['status'] == 4
+ end
+
+ def modify_payload(payload)
+ modified_payload = payload.clone()
+ modified_payload['commits'][0]['message'] << "\nDORA-1"
+ modified_payload['commits'][1]['message'] << "\nDORA-2 #fixed"
+ modified_payload['commits'][2]['message'] << "\nDORA-3 #closed"
+ return modified_payload
+ end
+
+ def service(*args)
+ super Service::Backlog, *args
+ end
+
+ class FakeXMLRPC
+ def call(procedure, arguments)
+ case procedure
+ when 'backlog.addComment'
+ commented << {'key' => arguments['key'], 'content' => arguments['content']}
+ when 'backlog.switchStatus'
+ switched << { 'key' => arguments['key'], 'comment' => arguments['comment'], 'status' => arguments['statusId']}
+ end
+ end
+
+ def commented
+ @commented ||= []
+ end
+
+ def switched
+ @switched ||= []
+ end
+ end
+end
+

0 comments on commit b3f3a0d

Please sign in to comment.