vx-service_connector 0.2.12 → 0.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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