txgh-server 1.0.0 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b61f30ae875a1c12d037ce7123cece67a1bd4a12
4
- data.tar.gz: 35536bb56e5cee37208c0c8a8f64e60347a0f88a
3
+ metadata.gz: 0a618f6c8f91abd2996e3e7589eeee8d644703f9
4
+ data.tar.gz: db946191d261517d34c339aa98424e46a72d0b0e
5
5
  SHA512:
6
- metadata.gz: 5d1655be5e922b9e6157bbc4791ca9dddddc8316df4459d4d3418e127076c2b8933de70dd43de9caab17a6d08a4e978fbde5455425ff43c62c3a832412caa3c0
7
- data.tar.gz: 1f6ebab2ba51bf04f9e49fb6d03afcb7a7faef63492bcd6a1d84672b7aa5a6a02b5cf43ddbc085ee39338106065ecac198fc4f862c5d97636ab2f5370d72d01b
6
+ metadata.gz: d09779408d47e28a776cbd8dc4e2ac36c1f9f56a9d2288f0beffddfc9f6ac3d84897b20dd968e5569506a47df5340e227adc499d888753a5ffcb9ff26e5ae33d
7
+ data.tar.gz: 38494763dc17346acb93efab18249efb1f9be9b03416b2ab1d8177fb6465c962a7b05e8541a4532af4bc785371f343a65d2a07f6362417be27157dba9ad4781c
@@ -1,3 +1,3 @@
1
1
  module TxghServer
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -0,0 +1,50 @@
1
+ module TxghServer
2
+ module Webhooks
3
+ module Github
4
+ class DeleteAttributes
5
+ ATTRIBUTES = [
6
+ :repo_name, :ref, :ref_type
7
+ ]
8
+
9
+ class << self
10
+ def from_webhook_payload(payload)
11
+ new(
12
+ ATTRIBUTES.each_with_object({}) do |attr, ret|
13
+ ret[attr] = public_send(attr, payload)
14
+ end
15
+ )
16
+ end
17
+
18
+ def repo_name(payload)
19
+ payload.fetch('repository').fetch('full_name')
20
+ end
21
+
22
+ def ref(payload)
23
+ payload.fetch('ref')
24
+ end
25
+
26
+ def ref_type(payload)
27
+ payload.fetch('ref_type')
28
+ end
29
+ end
30
+
31
+ attr_reader *ATTRIBUTES
32
+
33
+ def initialize(options = {})
34
+ ATTRIBUTES.each do |attr|
35
+ instance_variable_set(
36
+ "@#{attr}", options.fetch(attr) { options.fetch(attr.to_s) }
37
+ )
38
+ end
39
+ end
40
+
41
+ def to_h
42
+ ATTRIBUTES.each_with_object({}) do |attr, ret|
43
+ ret[attr] = public_send(attr)
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,9 +1,17 @@
1
1
  module TxghServer
2
2
  module Webhooks
3
3
  module Github
4
- class DeleteHandler < Handler
4
+ class DeleteHandler
5
+ include ResponseHelpers
5
6
 
6
- include Txgh::CategorySupport
7
+ attr_reader :project, :repo, :logger, :attributes
8
+
9
+ def initialize(project, repo, logger, attributes)
10
+ @project = project
11
+ @repo = repo
12
+ @logger = logger
13
+ @attributes = attributes
14
+ end
7
15
 
8
16
  def execute
9
17
  perform_delete if should_handle_request?
@@ -22,13 +30,13 @@ module TxghServer
22
30
 
23
31
  def should_handle_request?
24
32
  # ref_type can be either 'branch' or 'tag' - we only care about branches
25
- payload['ref_type'] == 'branch' &&
33
+ attributes.ref_type == 'branch' &&
26
34
  repo.should_process_ref?(branch) &&
27
35
  project.auto_delete_resources?
28
36
  end
29
37
 
30
38
  def branch
31
- Txgh::Utils.absolute_branch(payload['ref'].sub(/^refs\//, ''))
39
+ Txgh::Utils.absolute_branch(attributes.ref.sub(/^refs\//, ''))
32
40
  end
33
41
 
34
42
  end
@@ -6,7 +6,10 @@ module TxghServer
6
6
  class PingHandler
7
7
  include ResponseHelpers
8
8
 
9
- def initialize(options = {})
9
+ attr_reader :logger
10
+
11
+ def initialize(logger)
12
+ @logger = logger
10
13
  end
11
14
 
12
15
  def execute
@@ -0,0 +1,77 @@
1
+ require 'set'
2
+
3
+ module TxghServer
4
+ module Webhooks
5
+ module Github
6
+ class PushAttributes
7
+ ATTRIBUTES = [
8
+ :repo_name, :ref, :before, :after,
9
+ :added_files, :modified_files, :author
10
+ ]
11
+
12
+ class << self
13
+ def from_webhook_payload(payload)
14
+ new(
15
+ ATTRIBUTES.each_with_object({}) do |attr, ret|
16
+ ret[attr] = public_send(attr, payload)
17
+ end
18
+ )
19
+ end
20
+
21
+ def repo_name(payload)
22
+ payload.fetch('repository').fetch('full_name')
23
+ end
24
+
25
+ def ref(payload)
26
+ payload.fetch('ref')
27
+ end
28
+
29
+ def before(payload)
30
+ payload.fetch('before')
31
+ end
32
+
33
+ def after(payload)
34
+ payload.fetch('after')
35
+ end
36
+
37
+ def added_files(payload)
38
+ extract_files(payload, 'added')
39
+ end
40
+
41
+ def modified_files(payload)
42
+ extract_files(payload, 'modified')
43
+ end
44
+
45
+ def author(payload)
46
+ payload.fetch('head_commit').fetch('committer').fetch('name')
47
+ end
48
+
49
+ def extract_files(payload, state)
50
+ payload.fetch('commits').flat_map { |c| c[state] }.uniq
51
+ end
52
+ end
53
+
54
+ def files
55
+ @files ||= added_files + modified_files
56
+ end
57
+
58
+ attr_reader *ATTRIBUTES
59
+
60
+ def initialize(options = {})
61
+ ATTRIBUTES.each do |attr|
62
+ instance_variable_set(
63
+ "@#{attr}", options.fetch(attr) { options.fetch(attr.to_s) }
64
+ )
65
+ end
66
+ end
67
+
68
+ def to_h
69
+ ATTRIBUTES.each_with_object({}) do |attr, ret|
70
+ ret[attr] = public_send(attr)
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,10 +1,19 @@
1
- require 'base64'
2
1
  require 'set'
3
2
 
4
3
  module TxghServer
5
4
  module Webhooks
6
5
  module Github
7
- class PushHandler < Handler
6
+ class PushHandler
7
+ include ResponseHelpers
8
+
9
+ attr_reader :project, :repo, :logger, :attributes
10
+
11
+ def initialize(project, repo, logger, attributes)
12
+ @project = project
13
+ @repo = repo
14
+ @logger = logger
15
+ @attributes = attributes
16
+ end
8
17
 
9
18
  def execute
10
19
  # Check if the branch in the hook data is the configured branch we want
@@ -14,31 +23,11 @@ module TxghServer
14
23
  if should_process?
15
24
  logger.info('found branch in github request')
16
25
 
17
- tx_resources = tx_resources_for(branch)
18
-
19
- modified_resources = added_and_modified_resources_for(tx_resources)
20
- modified_resources += l10n_resources_for(tx_resources)
21
-
22
- if repo.github_config_branch.include?('tags/')
23
- modified_resources += tag_resources_for(tx_resources)
24
- end
25
-
26
- # Handle DBZ 'L10N' special case
27
- if branch.include?("L10N")
28
- logger.info('processing L10N tag')
29
-
30
- # Create a new branch off tag commit
31
- if branch.include?('tags/L10N')
32
- repo.api.create_ref(repo.name, 'heads/L10N', payload['head_commit']['id'])
33
- end
34
- end
35
-
36
26
  updater = Txgh::ResourceUpdater.new(project, repo, logger)
37
- categories = { 'author' => payload['head_commit']['committer']['name'] }
38
- ref = repo.api.get_ref(repo.name, branch)
27
+ categories = { 'author' => attributes.author }
39
28
 
40
- modified_resources.each do |resource|
41
- updater.update_resource(resource, ref[:object][:sha], categories)
29
+ added_and_modified_resources.each do |resource|
30
+ updater.update_resource(resource, categories)
42
31
  end
43
32
  end
44
33
 
@@ -47,47 +36,20 @@ module TxghServer
47
36
 
48
37
  private
49
38
 
50
- def tag_resources_for(tx_resources)
51
- payload['head_commit']['modified'].each_with_object(Set.new) do |modified, ret|
52
- logger.info("processing modified file: #{modified}")
53
-
54
- if tx_resources.include?(modified)
55
- ret << tx_resources[modified]
56
- end
57
- end
58
- end
59
-
60
- def l10n_resources_for(tx_resources)
61
- payload['head_commit']['modified'].each_with_object(Set.new) do |modified, ret|
62
- if tx_resources.include?(modified)
63
- logger.info("setting new resource: #{tx_resources[modified].L10N_resource_slug}")
64
- ret << tx_resources[modified]
65
- end
66
- end
67
- end
68
-
69
39
  # finds the resources that were updated in each commit
70
- def added_and_modified_resources_for(tx_resources)
71
- payload['commits'].each_with_object(Set.new) do |commit, ret|
72
- logger.info('processing commit')
40
+ def added_and_modified_resources
41
+ attributes.files.each_with_object(Set.new) do |file, ret|
42
+ logger.info("processing added/modified file: #{file}")
73
43
 
74
- (commit['modified'] + commit['added']).each do |file|
75
- logger.info("processing added/modified file: #{file}")
76
-
77
- if tx_resources.include?(file)
78
- ret << tx_resources[file]
79
- end
44
+ if tx_resources.include?(file)
45
+ ret << tx_resources[file]
80
46
  end
81
47
  end
82
48
  end
83
49
 
84
50
  # Build an index of known Tx resources, by source file
85
- def tx_resources_for(branch)
86
- tx_config.resources.each_with_object({}) do |resource, ret|
87
- logger.info('processing resource')
88
-
89
- # If we're processing by branch, create a branch resource. Otherwise,
90
- # use the original resource.
51
+ def tx_resources
52
+ @tx_resources ||= tx_config.resources.each_with_object({}) do |resource, ret|
91
53
  ret[resource.source_file] = if repo.process_all_branches?
92
54
  Txgh::TxBranchResource.new(resource, branch) # maybe find instead?
93
55
  else
@@ -101,7 +63,7 @@ module TxghServer
101
63
  end
102
64
 
103
65
  def branch
104
- @ref ||= payload['ref'].sub(/^refs\//, '')
66
+ @ref ||= attributes.ref.sub(/^refs\//, '')
105
67
  end
106
68
 
107
69
  def should_process?
@@ -115,7 +77,7 @@ module TxghServer
115
77
  def should_process_commit?
116
78
  # return false if 'after' commit sha is all zeroes (indicates branch
117
79
  # has been deleted)
118
- !(payload.fetch('after', '') =~ /\A0+\z/)
80
+ !((attributes.after || '') =~ /\A0+\z/)
119
81
  end
120
82
 
121
83
  end
@@ -4,48 +4,14 @@ module TxghServer
4
4
  module Webhooks
5
5
  module Github
6
6
  class RequestHandler
7
- class << self
8
-
9
- include ResponseHelpers
7
+ include ResponseHelpers
10
8
 
9
+ class << self
11
10
  def handle_request(request, logger)
12
- case request.env['HTTP_X_GITHUB_EVENT']
13
- when 'push'
14
- handle_push(request, logger)
15
- when 'delete'
16
- handle_delete(request, logger)
17
- when 'ping'
18
- handle_ping(request, logger)
19
- else
20
- handle_unexpected
21
- end
11
+ new(request, logger).handle_request
22
12
  end
23
-
24
- private
25
-
26
- def handle_push(request, logger)
27
- klass = TxghServer::Webhooks::Github::PushHandler
28
- new(request, logger).handle(klass)
29
- end
30
-
31
- def handle_delete(request, logger)
32
- klass = TxghServer::Webhooks::Github::DeleteHandler
33
- new(request, logger).handle(klass)
34
- end
35
-
36
- def handle_ping(request, logger)
37
- klass = TxghServer::Webhooks::Github::PingHandler
38
- new(request, logger).handle(klass)
39
- end
40
-
41
- def handle_unexpected
42
- respond_with_error(400, 'Unexpected event type')
43
- end
44
-
45
13
  end
46
14
 
47
- include ResponseHelpers
48
-
49
15
  attr_reader :request, :logger
50
16
 
51
17
  def initialize(request, logger)
@@ -53,16 +19,18 @@ module TxghServer
53
19
  @logger = logger
54
20
  end
55
21
 
56
- def handle(klass)
22
+ def handle_request
57
23
  handle_safely do
58
- handler = klass.new(
59
- project: config.transifex_project,
60
- repo: config.github_repo,
61
- payload: payload,
62
- logger: logger
63
- )
64
-
65
- handler.execute
24
+ case request.env['HTTP_X_GITHUB_EVENT']
25
+ when 'push'
26
+ handle_push
27
+ when 'delete'
28
+ handle_delete
29
+ when 'ping'
30
+ handle_ping
31
+ else
32
+ handle_unexpected
33
+ end
66
34
  end
67
35
  end
68
36
 
@@ -78,16 +46,38 @@ module TxghServer
78
46
  respond_with_error(500, "Internal server error: #{e.message}", e)
79
47
  end
80
48
 
49
+ def handle_push
50
+ attributes = PushAttributes.from_webhook_payload(payload)
51
+ PushHandler.new(project, repo, logger, attributes).execute
52
+ end
53
+
54
+ def handle_delete
55
+ attributes = DeleteAttributes.from_webhook_payload(payload)
56
+ DeleteHandler.new(project, repo, logger, attributes).execute
57
+ end
58
+
59
+ def handle_ping
60
+ PingHandler.new(logger).execute
61
+ end
62
+
63
+ def handle_unexpected
64
+ respond_with_error(400, 'Unexpected event type')
65
+ end
66
+
81
67
  def payload
82
- @payload ||= if request.params[:payload]
83
- JSON.parse(request.params[:payload])
84
- else
85
- JSON.parse(request.body.read)
68
+ @payload ||= begin
69
+ if request.params[:payload]
70
+ JSON.parse(request.params[:payload])
71
+ else
72
+ JSON.parse(request.body.read)
73
+ end
74
+ rescue JSON::ParserError
75
+ {}
86
76
  end
87
77
  end
88
78
 
89
79
  def github_repo_name
90
- payload['repository']['full_name']
80
+ payload.fetch('repository', {})['full_name']
91
81
  end
92
82
 
93
83
  def config
@@ -98,6 +88,10 @@ module TxghServer
98
88
  config.github_repo
99
89
  end
100
90
 
91
+ def project
92
+ config.transifex_project
93
+ end
94
+
101
95
  def authentic_request?
102
96
  if repo.webhook_protected?
103
97
  GithubRequestAuth.authentic_request?(
@@ -1,11 +1,13 @@
1
1
  module TxghServer
2
2
  module Webhooks
3
3
  module Github
4
- autoload :DeleteHandler, 'txgh-server/webhooks/github/delete_handler'
5
- autoload :Handler, 'txgh-server/webhooks/github/handler'
6
- autoload :PingHandler, 'txgh-server/webhooks/github/ping_handler'
7
- autoload :PushHandler, 'txgh-server/webhooks/github/push_handler'
8
- autoload :RequestHandler, 'txgh-server/webhooks/github/request_handler'
4
+ autoload :DeleteAttributes, 'txgh-server/webhooks/github/delete_attributes'
5
+ autoload :DeleteHandler, 'txgh-server/webhooks/github/delete_handler'
6
+ autoload :Handler, 'txgh-server/webhooks/github/handler'
7
+ autoload :PingHandler, 'txgh-server/webhooks/github/ping_handler'
8
+ autoload :PushHandler, 'txgh-server/webhooks/github/push_handler'
9
+ autoload :PushAttributes, 'txgh-server/webhooks/github/push_attributes'
10
+ autoload :RequestHandler, 'txgh-server/webhooks/github/request_handler'
9
11
  end
10
12
  end
11
13
  end
@@ -55,7 +55,7 @@ module TxghServer
55
55
 
56
56
  def tx_config
57
57
  @tx_config ||= Txgh::Config::TxManager.tx_config(project, repo, branch)
58
- rescue ConfigNotFoundError, TxghError
58
+ rescue Txgh::ConfigNotFoundError, Txgh::TxghError
59
59
  nil
60
60
  end
61
61
 
@@ -185,9 +185,9 @@ describe TxghServer::WebhookEndpoints do
185
185
 
186
186
  before(:each) do
187
187
  allow(TxghServer::Webhooks::Github::PushHandler).to(
188
- receive(:new) do |options|
189
- expect(options[:project].name).to eq(project_name)
190
- expect(options[:repo].name).to eq(repo_name)
188
+ receive(:new) do |project, repo, logger, attributes|
189
+ expect(project.name).to eq(project_name)
190
+ expect(repo.name).to eq(repo_name)
191
191
  handler
192
192
  end
193
193
  )
@@ -199,6 +199,7 @@ describe TxghServer::WebhookEndpoints do
199
199
  )
200
200
 
201
201
  payload = GithubPayloadBuilder.push_payload(repo_name, ref)
202
+ payload.add_commit
202
203
 
203
204
  sign_with payload.to_json
204
205
  header 'X-GitHub-Event', 'push'
@@ -217,8 +218,11 @@ describe TxghServer::WebhookEndpoints do
217
218
  end
218
219
 
219
220
  it 'returns invalid request if event unrecognized' do
221
+ payload = GithubPayloadBuilder.push_payload(repo_name, ref)
222
+
223
+ sign_with payload.to_json
220
224
  header 'X-GitHub-Event', 'foobar'
221
- post '/github'
225
+ post '/github', payload.to_json
222
226
 
223
227
  expect(last_response.status).to eq(400)
224
228
  end
@@ -8,7 +8,7 @@ require 'helpers/integration_setup'
8
8
  require 'uri'
9
9
  require 'yaml'
10
10
 
11
- include Txgh
11
+ include TxghServer
12
12
 
13
13
  describe 'hook integration tests', integration: true do
14
14
  include Rack::Test::Methods
@@ -83,10 +83,6 @@ describe 'hook integration tests', integration: true do
83
83
  File.read(payload_path.join('github_postbody_release.json'))
84
84
  end
85
85
 
86
- let(:github_postbody_l10n) do
87
- File.read(payload_path.join('github_postbody_l10n.json'))
88
- end
89
-
90
86
  let(:project_name) { 'test-project-88' }
91
87
  let(:repo_name) { 'txgh-bot/txgh-test-resources' }
92
88
 
@@ -146,14 +142,4 @@ describe 'hook integration tests', integration: true do
146
142
  expect(last_response).to be_ok
147
143
  end
148
144
  end
149
-
150
- it 'verifies the github l10n hook endpoint works' do
151
- VCR.use_cassette('github_l10n_hook_endpoint') do
152
- sign_github_request(github_postbody_l10n)
153
- header 'X-GitHub-Event', 'push'
154
- header 'content-type', 'application/x-www-form-urlencoded'
155
- post '/github', github_postbody_l10n
156
- expect(last_response).to be_ok
157
- end
158
- end
159
145
  end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'helpers/github_payload_builder'
3
+
4
+ include TxghServer
5
+ include TxghServer::Webhooks::Github
6
+
7
+ describe DeleteAttributes do
8
+ let(:repo_name) { 'my_repo' }
9
+ let(:ref) { 'heads/my_ref' }
10
+
11
+ let(:payload) do
12
+ GithubPayloadBuilder.delete_payload(repo_name, ref)
13
+ end
14
+
15
+ describe '#from_webhook_payload' do
16
+ let(:attributes) { DeleteAttributes.from_webhook_payload(payload.to_h) }
17
+
18
+ it 'pulls out repo name' do
19
+ expect(attributes.repo_name).to eq(repo_name)
20
+ end
21
+
22
+ it 'pulls out ref' do
23
+ expect(attributes.ref).to eq("refs/#{ref}")
24
+ end
25
+
26
+ it 'pulls out ref type' do
27
+ expect(attributes.ref_type).to eq('branch')
28
+ end
29
+ end
30
+ end
@@ -9,12 +9,11 @@ describe DeleteHandler do
9
9
  include StandardTxghSetup
10
10
 
11
11
  let(:handler) do
12
- DeleteHandler.new(
13
- project: transifex_project,
14
- repo: github_repo,
15
- payload: payload.to_h,
16
- logger: logger
17
- )
12
+ DeleteHandler.new(transifex_project, github_repo, logger, attributes)
13
+ end
14
+
15
+ let(:attributes) do
16
+ DeleteAttributes.from_webhook_payload(payload.to_h)
18
17
  end
19
18
 
20
19
  let(:payload) do
@@ -1,11 +1,12 @@
1
1
  require 'spec_helper'
2
+ require 'helpers/nil_logger'
2
3
 
3
4
  include TxghServer
4
5
  include TxghServer::Webhooks::Github
5
6
 
6
7
  describe PingHandler do
7
8
  let(:handler) do
8
- PingHandler.new
9
+ PingHandler.new(NilLogger.new)
9
10
  end
10
11
 
11
12
  it 'responds with a 200 success' do
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'helpers/github_payload_builder'
3
+
4
+ include TxghServer
5
+ include TxghServer::Webhooks::Github
6
+
7
+ describe PushAttributes do
8
+ let(:repo_name) { 'my_repo' }
9
+ let(:ref) { 'heads/my_ref' }
10
+ let(:added) { ['added_file.txt'] }
11
+ let(:modified) { ['modified_file.txt'] }
12
+
13
+ let(:payload) do
14
+ GithubPayloadBuilder.push_payload(repo_name, ref).tap do |payload|
15
+ payload.add_commit(added: added, modified: modified)
16
+ end
17
+ end
18
+
19
+ describe '#from_webhook_payload' do
20
+ let(:attributes) { PushAttributes.from_webhook_payload(payload.to_h) }
21
+
22
+ it 'pulls out repo name' do
23
+ expect(attributes.repo_name).to eq(repo_name)
24
+ end
25
+
26
+ it 'pulls out ref' do
27
+ expect(attributes.ref).to eq("refs/#{ref}")
28
+ end
29
+
30
+ it 'pulls out before sha' do
31
+ expect(attributes.before).to eq(payload.to_h['before'])
32
+ end
33
+
34
+ it 'pulls out after sha' do
35
+ expect(attributes.after).to eq(payload.to_h['after'])
36
+ end
37
+
38
+ it 'pulls out added files' do
39
+ expect(attributes.added_files.to_a).to eq(added)
40
+ end
41
+
42
+ it 'pulls out modified files' do
43
+ expect(attributes.modified_files.to_a).to eq(modified)
44
+ end
45
+
46
+ it 'combines all files into one handy array' do
47
+ expect(attributes.files.sort).to eq((added + modified).sort)
48
+ end
49
+
50
+ it 'pulls out the author' do
51
+ expect(attributes.author).to eq('Test User')
52
+ end
53
+ end
54
+ end