vx-service_connector 0.2.12 → 0.2.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/lib/vx/service_connector.rb +3 -0
  4. data/lib/vx/service_connector/bitbucket.rb +51 -0
  5. data/lib/vx/service_connector/bitbucket/commits.rb +30 -0
  6. data/lib/vx/service_connector/bitbucket/deploy_keys.rb +29 -0
  7. data/lib/vx/service_connector/bitbucket/files.rb +21 -0
  8. data/lib/vx/service_connector/bitbucket/hooks.rb +58 -0
  9. data/lib/vx/service_connector/bitbucket/notices.rb +11 -0
  10. data/lib/vx/service_connector/bitbucket/payload.rb +136 -0
  11. data/lib/vx/service_connector/bitbucket/repos.rb +56 -0
  12. data/lib/vx/service_connector/bitbucket/session.rb +113 -0
  13. data/lib/vx/service_connector/error.rb +1 -0
  14. data/lib/vx/service_connector/version.rb +1 -1
  15. data/spec/fixtures/bitbucket/add_deploy_key.json +7 -0
  16. data/spec/fixtures/bitbucket/commit.json +72 -0
  17. data/spec/fixtures/bitbucket/create_hook.json +12 -0
  18. data/spec/fixtures/bitbucket/deploy_keys.json +1 -0
  19. data/spec/fixtures/bitbucket/hooks.json +38 -0
  20. data/spec/fixtures/bitbucket/payload/created_pull_request.json +120 -0
  21. data/spec/fixtures/bitbucket/payload/declined_pull_request.json +80 -0
  22. data/spec/fixtures/bitbucket/payload/foreign_pull_request.json +120 -0
  23. data/spec/fixtures/bitbucket/payload/push.json +38 -0
  24. data/spec/fixtures/bitbucket/payload/updated_pull_request.json +80 -0
  25. data/spec/fixtures/bitbucket/repos.json +206 -0
  26. data/spec/fixtures/bitbucket/user_privileges.json +6 -0
  27. data/spec/fixtures/bitbucket/user_repo.json +63 -0
  28. data/spec/fixtures/bitbucket/user_repos.json +197 -0
  29. data/spec/lib/bitbucket_payload_spec.rb +82 -0
  30. data/spec/lib/bitbucket_spec.rb +144 -0
  31. data/spec/support/bitbucket_web_mocks.rb +87 -0
  32. data/vx-service_connector.gemspec +4 -3
  33. metadata +59 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5b071bc9825ff7e1abdfc7a258f11cf19234bde
4
- data.tar.gz: ad9c9f8ac7c25bfda55e2f691d6bc6f89d3ce2fc
3
+ metadata.gz: 54d71cae077981d1705c726931668bf9b2340cb9
4
+ data.tar.gz: c0dea00e6d39a55757f96b6bd6bc6cb9e4428182
5
5
  SHA512:
6
- metadata.gz: 8da6953d253b5584a2533ca4f9c1a23b3400d77bb08c56ce0f1e239f44720198d0da9168a61130fb5420ec2e30bcae76cb7f2b98d19272d1145b47d06de1c3ce
7
- data.tar.gz: 42bfeb21d21e951762be1b1e24febbe4670b690a58390f57680684f3e65736727c538727cf0486d18dff56257a83f5295e72b2d21f4354a26e06c503786af796
6
+ metadata.gz: bbb12a18dd04bb71320c037805d831f1e809fbe32b0d91055dfdcbd4287b6fd395dacb32fc67df1509f4654c0b19af8910f02b3bfc323f79910d4affc54d82da
7
+ data.tar.gz: 07315c7f4b03f472551857bbda073f7a8c934dce4fe324b15730194243fc5dd4d7de0fcf72089c08bf3ba34dca0e97d6ee9d7f9f2a33f2f8ee5339bb293a5fce
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in vx-service_connector.gemspec
4
4
  gemspec
5
+
6
+ gem 'pry'
@@ -9,6 +9,7 @@ module Vx
9
9
  autoload :Github, File.expand_path("../service_connector/github", __FILE__)
10
10
  autoload :GitlabV5, File.expand_path("../service_connector/gitlab_v5", __FILE__)
11
11
  autoload :GitlabV6, File.expand_path("../service_connector/gitlab_v6", __FILE__)
12
+ autoload :Bitbucket, File.expand_path("../service_connector/bitbucket", __FILE__)
12
13
  autoload :Model, File.expand_path("../service_connector/model", __FILE__)
13
14
 
14
15
  extend self
@@ -19,6 +20,8 @@ module Vx
19
20
  Github
20
21
  when :gitlab_v6
21
22
  GitlabV6
23
+ when :bitbucket
24
+ Bitbucket
22
25
  else
23
26
  raise ArgumentError, "Serivice for #{name.inspect} is not defined"
24
27
  end
@@ -0,0 +1,51 @@
1
+ module Vx
2
+ module ServiceConnector
3
+ Bitbucket = Struct.new(:login, :options) do
4
+
5
+ include ServiceConnector::Base
6
+
7
+ def repos
8
+ Bitbucket::Repos.new(session).to_a
9
+ end
10
+
11
+ def organizations
12
+ []
13
+ end
14
+
15
+ def hooks(repo)
16
+ Bitbucket::Hooks.new(session, repo)
17
+ end
18
+
19
+ def deploy_keys(repo)
20
+ Bitbucket::DeployKeys.new(session, repo)
21
+ end
22
+
23
+ def notices(repo)
24
+ Bitbucket::Notices.new(session, repo)
25
+ end
26
+
27
+ def files(repo)
28
+ Bitbucket::Files.new(session, repo)
29
+ end
30
+
31
+ def payload(repo, params)
32
+ Bitbucket::Payload.new(session, params).build
33
+ end
34
+
35
+ def commits(repo, options = {})
36
+ Bitbucket::Commits.new(session, repo)
37
+ end
38
+
39
+ private
40
+
41
+ def create_session
42
+ self.class::Session.new(login, options)
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+
49
+ Dir[File.expand_path("../bitbucket/*.rb", __FILE__)].each do |f|
50
+ require f
51
+ end
@@ -0,0 +1,30 @@
1
+ module Vx
2
+ module ServiceConnector
3
+ class Bitbucket
4
+ Commits = Struct.new(:session, :repo) do
5
+ def last(options = {})
6
+ begin
7
+ commits = session.get "api/1.0/repositories/#{repo.full_name}/changesets/?limit=1"
8
+ commit = commits["changesets"].first
9
+ author, email = commit["raw_author"].split(/[<>]/)
10
+ sha = commit["raw_node"]
11
+ branch = commit["branch"] || 'master'
12
+ Model::Payload.from_hash(
13
+ skip: false,
14
+ pull_request?: false,
15
+ branch: branch,
16
+ branch_label: branch,
17
+ sha: sha,
18
+ message: commit["message"],
19
+ author: author.strip,
20
+ author_email: email,
21
+ web_url: session.endpoint.to_s + "/#{repo.full_name}/commits/#{sha}"
22
+ )
23
+ rescue RequestError
24
+ nil
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ module Vx
2
+ module ServiceConnector
3
+ class Bitbucket
4
+ DeployKeys = Struct.new(:session, :repo) do
5
+
6
+ def all
7
+ begin
8
+ session.get "api/1.0/repositories/#{repo.full_name}/deploy-keys?pagelen=100"
9
+ rescue RequestError
10
+ []
11
+ end
12
+ end
13
+
14
+ def create(key_name, public_key)
15
+ session.post "api/1.0/repositories/#{repo.full_name}/deploy-keys", label: key_name, key: public_key
16
+ end
17
+
18
+ def destroy(key_name)
19
+ all.select do |key|
20
+ key['label'] == key_name
21
+ end.map do |key|
22
+ session.delete "api/1.0/repositories/#{repo.full_name}/deploy-keys/#{key['pk']}"
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ require 'base64'
2
+
3
+ module Vx
4
+ module ServiceConnector
5
+ class Bitbucket
6
+ Files = Struct.new(:session, :repo) do
7
+
8
+ def get(sha, path)
9
+ begin
10
+ re = session.get("api/1.0/repositories/#{repo.full_name}/src/#{sha}/#{path}")
11
+ re['source']
12
+ rescue RequestError => e
13
+ $stderr.puts "ERROR: #{e.inspect}"
14
+ nil
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,58 @@
1
+ module Vx
2
+ module ServiceConnector
3
+ class Bitbucket
4
+ Hooks = Struct.new(:session, :repo) do
5
+
6
+ def all
7
+ begin
8
+ session.get hooks_url
9
+ rescue RequestError
10
+ []
11
+ end
12
+ end
13
+
14
+ def create(url, token)
15
+ session.post(
16
+ hooks_url,
17
+ 'type' => 'POST',
18
+ 'URL' => url
19
+ )
20
+ session.post(
21
+ hooks_url,
22
+ 'type' => 'Pull Request POST',
23
+ 'URL' => url,
24
+ 'create/edit/merge/decline' => 'on',
25
+ 'comments' => 'off',
26
+ 'approve/unapprove' => 'off'
27
+ )
28
+ end
29
+
30
+ def destroy(url_mask)
31
+ all.select do |hook|
32
+ url = extract_url(hook)
33
+ url && url =~ /#{Regexp.escape url_mask}/
34
+ end.map do |hook|
35
+ session.delete hook_url(hook['id'])
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def extract_url(hook)
42
+ fields = hook['service']['fields']
43
+ url = fields.select{|f| f['name'] == 'URL' }.map{|f| f['value'] }.first
44
+ url
45
+ end
46
+
47
+ def hooks_url
48
+ "api/1.0/repositories/#{repo.full_name}/services"
49
+ end
50
+
51
+ def hook_url(id)
52
+ "#{hooks_url}/#{id}"
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,11 @@
1
+ module Vx
2
+ module ServiceConnector
3
+ class Bitbucket
4
+ Notices = Struct.new(:session, :repo) do
5
+ def create(build_sha, build_status, build_url, description)
6
+ :not_available
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,136 @@
1
+ module Vx
2
+ module ServiceConnector
3
+ class Bitbucket
4
+ Payload = Struct.new(:session, :params) do
5
+
6
+ def build
7
+ ServiceConnector::Model::Payload.new(
8
+ !!ignore?,
9
+ pull_request?,
10
+ pull_request_number,
11
+ branch,
12
+ branch_label,
13
+ sha,
14
+ message,
15
+ author,
16
+ author_email,
17
+ web_url
18
+ )
19
+ end
20
+
21
+ private
22
+
23
+ def pull_request?
24
+ !key? 'repository'
25
+ end
26
+
27
+ def pull_request_number
28
+ if pull_request? && pull_request.key?('id')
29
+ pull_request['id']
30
+ end
31
+ end
32
+
33
+ def sha
34
+ if pull_request?
35
+ pull_request['source']['commit']['hash']
36
+ else
37
+ head_commit['raw_node']
38
+ end
39
+ end
40
+
41
+ def branch
42
+ @branch ||= begin
43
+ if pull_request?
44
+ pull_request['source']['branch']['name']
45
+ else
46
+ head_commit['branch']
47
+ end
48
+ end
49
+ end
50
+
51
+ def branch_label
52
+ branch
53
+ end
54
+
55
+ def web_url
56
+ if pull_request?
57
+ pull_request['links'] ? pull_request['links']['html']['href'] : nil
58
+ else
59
+ "https://bitbucket.org#{params['repository']['absolute_url']}commits/#{head_commit['raw_node']}"
60
+ end
61
+ end
62
+
63
+ def message
64
+ if pull_request?
65
+ commit_for_pull_request["message"]
66
+ else
67
+ head_commit['message']
68
+ end
69
+ end
70
+
71
+ def author
72
+ if pull_request?
73
+ commit_for_pull_request["author"]["user"]["display_name"]
74
+ else
75
+ head_commit['author']
76
+ end
77
+ end
78
+
79
+ def author_email
80
+ if pull_request?
81
+ commit_for_pull_request["author"]["raw"][/.*<([^>]*)/,1]
82
+ else
83
+ head_commit['raw_author'][/.*<([^>]*)/,1]
84
+ end
85
+ end
86
+
87
+ def close_pull_request?
88
+ pull_request? && (pull_request['state'] && %w(DECLINED MERGED).include?(pull_request['state']))
89
+ end
90
+
91
+ def pull_request_head_repo_full_name
92
+ pull_request['source']['repository']['full_name']
93
+ end
94
+
95
+ def pull_request_base_repo_full_name
96
+ pull_request['destination']['repository']['full_name']
97
+ end
98
+
99
+ def foreign_pull_request?
100
+ pull_request_head_repo_full_name != pull_request_base_repo_full_name
101
+ end
102
+
103
+ def ignore?
104
+ if pull_request?
105
+ close_pull_request? || !foreign_pull_request?
106
+ else
107
+ sha == '0000000000000000000000000000000000000000'
108
+ end
109
+ end
110
+
111
+ def commit_for_pull_request
112
+ @commit_for_pull_request ||= begin
113
+ session.get pull_request['source']['commit']['links']['self']['href']
114
+ end
115
+ end
116
+
117
+ def head_commit
118
+ params['commits'].last || {}
119
+ end
120
+
121
+ def pull_request
122
+ params.first.last
123
+ end
124
+
125
+ def key?(name)
126
+ params.key? name
127
+ end
128
+
129
+ def [](val)
130
+ params[val]
131
+ end
132
+
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,56 @@
1
+ module Vx
2
+ module ServiceConnector
3
+ class Bitbucket
4
+ Repos = Struct.new(:session) do
5
+
6
+ def to_a
7
+ @repos ||= user_repositories
8
+ end
9
+
10
+ private
11
+
12
+ def user_repositories
13
+ res = session.get("api/1.0/user/repositories")
14
+ res.select do |repo|
15
+ git?(repo) && repo_access?(repo['owner'])
16
+ end.map do |repo|
17
+ repo_to_model repo
18
+ end
19
+ end
20
+
21
+ def repo_to_model(repo)
22
+ name = repo['owner'] + "/" + repo['slug']
23
+ Model::Repo.new(
24
+ name,
25
+ name,
26
+ repo['is_private'],
27
+ "git@#{session.endpoint.host}/#{name}.git",
28
+ "#{session.endpoint}/#{name}",
29
+ repo['description']
30
+ )
31
+ end
32
+
33
+ def team_admin
34
+ @team_admin ||= begin
35
+ values = session.get("api/1.0/user/privileges")['teams']
36
+ values.select{|k,v| v == 'admin' }.keys
37
+ end
38
+ end
39
+
40
+ def login
41
+ session.login
42
+ end
43
+
44
+ def git?(repo)
45
+ repo['scm'] == 'git'
46
+ end
47
+
48
+ def repo_access?(repo_owner)
49
+ repo_owner == login ||
50
+ team_admin.include?(repo_owner)
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,113 @@
1
+ require 'oauth'
2
+ require 'json'
3
+ require 'ostruct'
4
+ require 'uri'
5
+
6
+ module Vx
7
+ module ServiceConnector
8
+ class Bitbucket
9
+ Session = Struct.new(:login, :options) do
10
+
11
+ def get(url)
12
+ wrap do
13
+ res = agent.get request_url(url)
14
+ response! res
15
+ end
16
+ end
17
+
18
+ def post(url, options = {})
19
+ wrap do
20
+ res = agent.post request_url(url), options
21
+ response! res
22
+ end
23
+ end
24
+
25
+ def delete(url)
26
+ wrap do
27
+ res = agent.delete request_url(url)
28
+ response! res
29
+ end
30
+ end
31
+
32
+ def endpoint
33
+ @endpoint ||= URI("https://bitbucket.org")
34
+ end
35
+
36
+ def self.test
37
+ {
38
+ consumer_key: "key",
39
+ consumer_secret: "secret",
40
+ token: "token",
41
+ token_secret: "token secret"
42
+ }
43
+ end
44
+
45
+ private
46
+
47
+
48
+ def request_url(url)
49
+ if url.include?(endpoint.to_s)
50
+ url
51
+ else
52
+ "#{endpoint}/#{url}"
53
+ end
54
+ end
55
+
56
+ def response!(res)
57
+ if (200..204).include?(res.code.to_i)
58
+ if res.header['Content-Type'].include?("application/json")
59
+ ::JSON.parse(res.body)
60
+ else
61
+ res.body
62
+ end
63
+ else
64
+ raise RequestError, res.body
65
+ end
66
+ end
67
+
68
+ def wrap
69
+ begin
70
+ yield
71
+ rescue Errno::ETIMEDOUT => e
72
+ raise RequestError, e
73
+ end
74
+ end
75
+
76
+ def validate_options!
77
+ unless options.is_a?(Hash) && options.keys.sort == [:consumer_key, :consumer_secret, :token, :token_secret]
78
+ raise InvalidArguments, options
79
+ end
80
+ end
81
+
82
+ def agent
83
+ @agent ||= begin
84
+ validate_options!
85
+ consumer = OAuth::Consumer.new(
86
+ options[:consumer_key], options[:consumer_secret],
87
+ site: endpoint.to_s
88
+ )
89
+ token = OAuth::AccessToken.new consumer, options[:token], options[:token_secret]
90
+ =begin
91
+ Sawyer::Agent.new(endpoint) do |http|
92
+ http.headers['content-type'] = 'application/json'
93
+ http.headers['accept'] = 'application/json'
94
+ http.ssl.verify = false
95
+ http.options[:timeout] = 5
96
+ http.options[:open_timeout] = 5
97
+ http.request :oauth,
98
+ consumer_key: "7j4RYZEd8YTQdfTWkJ",
99
+ consumer_secret: "Y8fSdaDK4GxKzvJn3tKRzyyYXctYSbUV",
100
+ token: "WhFvryHuZ82XLKW6aP",
101
+ token_secret: "zLSQe2DwhA4mfzwnPA53pKqLSyZgX5XH"
102
+ ignore_extra_keys: true
103
+ http.response :logger
104
+ end
105
+ =end
106
+ token
107
+ end
108
+ end
109
+
110
+ end
111
+ end
112
+ end
113
+ end