txgh 5.3.4 → 5.4.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/lib/txgh/resource_deleter.rb +50 -0
  3. data/lib/txgh/version.rb +1 -1
  4. data/lib/txgh.rb +1 -6
  5. data/spec/resource_deleter_spec.rb +48 -0
  6. data/spec/spec_helper.rb +0 -8
  7. data/txgh.gemspec +1 -5
  8. metadata +6 -113
  9. data/lib/ext/zipline/output_stream.rb +0 -62
  10. data/lib/txgh/app.rb +0 -140
  11. data/lib/txgh/github_request_auth.rb +0 -28
  12. data/lib/txgh/handlers/download_handler.rb +0 -87
  13. data/lib/txgh/handlers/github/delete_handler.rb +0 -65
  14. data/lib/txgh/handlers/github/handler.rb +0 -20
  15. data/lib/txgh/handlers/github/ping_handler.rb +0 -18
  16. data/lib/txgh/handlers/github/push_handler.rb +0 -124
  17. data/lib/txgh/handlers/github/request_handler.rb +0 -113
  18. data/lib/txgh/handlers/github.rb +0 -11
  19. data/lib/txgh/handlers/response.rb +0 -17
  20. data/lib/txgh/handlers/stream_response.rb +0 -39
  21. data/lib/txgh/handlers/tgz_stream_response.rb +0 -41
  22. data/lib/txgh/handlers/transifex/hook_handler.rb +0 -94
  23. data/lib/txgh/handlers/transifex/request_handler.rb +0 -78
  24. data/lib/txgh/handlers/transifex.rb +0 -8
  25. data/lib/txgh/handlers/triggers/handler.rb +0 -52
  26. data/lib/txgh/handlers/triggers/pull_handler.rb +0 -20
  27. data/lib/txgh/handlers/triggers/push_handler.rb +0 -20
  28. data/lib/txgh/handlers/triggers.rb +0 -9
  29. data/lib/txgh/handlers/zip_stream_response.rb +0 -21
  30. data/lib/txgh/handlers.rb +0 -12
  31. data/lib/txgh/transifex_request_auth.rb +0 -53
  32. data/spec/app_spec.rb +0 -347
  33. data/spec/github_request_auth_spec.rb +0 -39
  34. data/spec/handlers/download_handler_spec.rb +0 -91
  35. data/spec/handlers/github/delete_handler_spec.rb +0 -71
  36. data/spec/handlers/github/ping_handler_spec.rb +0 -16
  37. data/spec/handlers/github/push_handler_spec.rb +0 -106
  38. data/spec/handlers/tgz_stream_response_spec.rb +0 -59
  39. data/spec/handlers/transifex/hook_handler_spec.rb +0 -136
  40. data/spec/handlers/zip_stream_response_spec.rb +0 -58
  41. data/spec/helpers/github_payload_builder.rb +0 -141
  42. data/spec/helpers/integration_setup.rb +0 -47
  43. data/spec/integration/cassettes/github_l10n_hook_endpoint.yml +0 -536
  44. data/spec/integration/cassettes/pull.yml +0 -47
  45. data/spec/integration/cassettes/push.yml +0 -544
  46. data/spec/integration/cassettes/transifex_hook_endpoint.yml +0 -221
  47. data/spec/integration/config/tx.config +0 -10
  48. data/spec/integration/hooks_spec.rb +0 -159
  49. data/spec/integration/payloads/github_postbody.json +0 -161
  50. data/spec/integration/payloads/github_postbody_l10n.json +0 -136
  51. data/spec/integration/payloads/github_postbody_release.json +0 -136
  52. data/spec/integration/triggers_spec.rb +0 -45
  53. data/spec/transifex_request_auth_spec.rb +0 -39
@@ -1,78 +0,0 @@
1
- require 'uri'
2
-
3
- module Txgh
4
- module Handlers
5
- module Transifex
6
- class RequestHandler
7
- class << self
8
-
9
- def handle_request(request, logger)
10
- new(request, logger).handle(Txgh::Handlers::Transifex::HookHandler)
11
- end
12
-
13
- end
14
-
15
- include ResponseHelpers
16
-
17
- attr_reader :request, :logger
18
-
19
- def initialize(request, logger)
20
- @request = request
21
- @logger = logger
22
- end
23
-
24
- def handle(klass)
25
- handle_safely do
26
- handler = klass.new(
27
- project: config.transifex_project,
28
- repo: config.github_repo,
29
- resource_slug: payload['resource'],
30
- language: payload['language'],
31
- logger: logger
32
- )
33
-
34
- handler.execute
35
- end
36
- end
37
-
38
- private
39
-
40
- def handle_safely
41
- if authentic_request?
42
- yield
43
- else
44
- respond_with_error(401, 'Unauthorized')
45
- end
46
- rescue => e
47
- respond_with_error(500, "Internal server error: #{e.message}", e)
48
- end
49
-
50
- def authentic_request?
51
- if project.webhook_protected?
52
- TransifexRequestAuth.authentic_request?(
53
- request, project.webhook_secret
54
- )
55
- else
56
- true
57
- end
58
- end
59
-
60
- def project
61
- config.transifex_project
62
- end
63
-
64
- def config
65
- @config ||= Txgh::Config::KeyManager.config_from_project(payload['project'])
66
- end
67
-
68
- def payload
69
- @payload ||= begin
70
- request.body.rewind
71
- Hash[URI.decode_www_form(request.body.read)]
72
- end
73
- end
74
-
75
- end
76
- end
77
- end
78
- end
@@ -1,8 +0,0 @@
1
- module Txgh
2
- module Handlers
3
- module Transifex
4
- autoload :HookHandler, 'txgh/handlers/transifex/hook_handler'
5
- autoload :RequestHandler, 'txgh/handlers/transifex/request_handler'
6
- end
7
- end
8
- end
@@ -1,52 +0,0 @@
1
- module Txgh
2
- module Handlers
3
- module Triggers
4
- class Handler
5
-
6
- # includes response helpers in both the class and the singleton class
7
- include ResponseHelpers
8
-
9
- class << self
10
- def handle_request(request, logger)
11
- handle_safely do
12
- config = Txgh::Config::KeyManager.config_from_project(
13
- request.params.fetch('project_slug')
14
- )
15
-
16
- handler_for(config, request, logger).execute
17
- end
18
- end
19
-
20
- private
21
-
22
- def handle_safely
23
- yield
24
- rescue => e
25
- respond_with_error(500, "Internal server error: #{e.message}", e)
26
- end
27
-
28
- def handler_for(config, request, logger)
29
- new(
30
- project: config.transifex_project,
31
- repo: config.github_repo,
32
- branch: request.params.fetch('branch'),
33
- resource_slug: request.params.fetch('resource_slug'),
34
- logger: logger
35
- )
36
- end
37
- end
38
-
39
- attr_reader :project, :repo, :branch, :resource_slug, :logger
40
-
41
- def initialize(options = {})
42
- @project = options[:project]
43
- @repo = options[:repo]
44
- @branch = Utils.absolute_branch(options[:branch])
45
- @resource_slug = options[:resource_slug]
46
- @logger = options[:logger]
47
- end
48
-
49
- end
50
- end
51
- end
52
- end
@@ -1,20 +0,0 @@
1
- module Txgh
2
- module Handlers
3
- module Triggers
4
- class PullHandler < Handler
5
-
6
- def execute
7
- puller.pull_slug(resource_slug)
8
- respond_with(200, true)
9
- end
10
-
11
- private
12
-
13
- def puller
14
- @puller ||= Txgh::Puller.new(project, repo, branch)
15
- end
16
-
17
- end
18
- end
19
- end
20
- end
@@ -1,20 +0,0 @@
1
- module Txgh
2
- module Handlers
3
- module Triggers
4
- class PushHandler < Handler
5
-
6
- def execute
7
- pusher.push_slug(resource_slug)
8
- respond_with(200, true)
9
- end
10
-
11
- private
12
-
13
- def pusher
14
- @pusher ||= Txgh::Pusher.new(project, repo, branch)
15
- end
16
-
17
- end
18
- end
19
- end
20
- end
@@ -1,9 +0,0 @@
1
- module Txgh
2
- module Handlers
3
- module Triggers
4
- autoload :Handler, 'txgh/handlers/triggers/handler'
5
- autoload :PullHandler, 'txgh/handlers/triggers/pull_handler'
6
- autoload :PushHandler, 'txgh/handlers/triggers/push_handler'
7
- end
8
- end
9
- end
@@ -1,21 +0,0 @@
1
- require 'ext/zipline/output_stream'
2
-
3
- module Txgh
4
- module Handlers
5
- class ZipStreamResponse < StreamResponse
6
-
7
- def write_to(stream)
8
- Zipline::OutputStream.open(stream) do |zipfile|
9
- enum.each do |file_name, contents|
10
- zipfile.put_next_entry(file_name, contents.bytesize)
11
- zipfile << contents
12
- end
13
- end
14
- end
15
-
16
- def file_extension
17
- '.zip'
18
- end
19
- end
20
- end
21
- end
data/lib/txgh/handlers.rb DELETED
@@ -1,12 +0,0 @@
1
- module Txgh
2
- module Handlers
3
- autoload :DownloadHandler, 'txgh/handlers/download_handler'
4
- autoload :Github, 'txgh/handlers/github'
5
- autoload :Response, 'txgh/handlers/response'
6
- autoload :StreamResponse, 'txgh/handlers/stream_response'
7
- autoload :TgzStreamResponse, 'txgh/handlers/tgz_stream_response'
8
- autoload :Transifex, 'txgh/handlers/transifex'
9
- autoload :Triggers, 'txgh/handlers/triggers'
10
- autoload :ZipStreamResponse, 'txgh/handlers/zip_stream_response'
11
- end
12
- end
@@ -1,53 +0,0 @@
1
- require 'openssl'
2
-
3
- module Txgh
4
- class TransifexRequestAuth
5
- HMAC_DIGEST = OpenSSL::Digest.new('sha1')
6
- RACK_HEADER = 'HTTP_X_TX_SIGNATURE'
7
- TRANSIFEX_HEADER = 'X-TX-Signature'
8
-
9
- class << self
10
- def authentic_request?(request, secret)
11
- request.body.rewind
12
- expected_signature = header_value(request.body.read, secret)
13
- actual_signature = request.env[RACK_HEADER]
14
- actual_signature == expected_signature
15
- end
16
-
17
- def header_value(content, secret)
18
- digest(transform(content), secret)
19
- end
20
-
21
- private
22
-
23
- # In order to generate a correct HMAC hash, the request body must be
24
- # parsed and made to look like a python map. If you're thinking that's
25
- # weird, you're correct, but it's apparently expected behavior.
26
- def transform(content)
27
- params = URI.decode_www_form(content)
28
-
29
- params = params.map do |key, val|
30
- key = "'#{key}'"
31
- val = interpret_val(val)
32
- "#{key}: #{val}"
33
- end
34
-
35
- "{#{params.join(', ')}}"
36
- end
37
-
38
- def interpret_val(val)
39
- if val =~ /\A[\d]+\z/
40
- val
41
- else
42
- "u'#{val}'"
43
- end
44
- end
45
-
46
- def digest(content, secret)
47
- Base64.encode64(
48
- OpenSSL::HMAC.digest(HMAC_DIGEST, secret, content)
49
- ).strip
50
- end
51
- end
52
- end
53
- end
data/spec/app_spec.rb DELETED
@@ -1,347 +0,0 @@
1
- require 'spec_helper'
2
- require 'rack/test'
3
- require 'uri'
4
-
5
- require 'helpers/github_payload_builder'
6
- require 'helpers/standard_txgh_setup'
7
-
8
- describe Txgh::Application do
9
- include Rack::Test::Methods
10
- include StandardTxghSetup
11
-
12
- def app
13
- Txgh::Application
14
- end
15
-
16
- describe '/health_check' do
17
- it 'indicates the server is running, returns a 200' do
18
- get '/health_check'
19
- expect(last_response).to be_ok
20
- expect(JSON.parse(last_response.body)).to eq({})
21
- end
22
- end
23
-
24
- describe '/config' do
25
- it 'fetches and returns the config for the given project' do
26
- get '/config', project_slug: project_name
27
- config = JSON.parse(last_response.body)['data']
28
- expect(config).to include('resources')
29
- expect(config).to_not include('branch_slug')
30
- expect(config['resources'].first).to eq(
31
- 'project_slug' => 'my_awesome_project',
32
- 'resource_slug' => 'my_resource',
33
- 'type' => 'YML',
34
- 'source_lang' => 'en',
35
- 'source_file' => 'sample.yml',
36
- 'translation_file' => 'translations/<lang>/sample.yml'
37
- )
38
- end
39
-
40
- it 'fetches and returns the config for the given project and branch' do
41
- get '/config', project_slug: project_name, branch: branch
42
- config = JSON.parse(last_response.body)['data']
43
- expect(config).to include('resources')
44
- expect(config['branch_slug']).to eq('heads_master')
45
- expect(config['resources'].first).to eq(
46
- 'project_slug' => 'my_awesome_project',
47
- 'resource_slug' => 'my_resource',
48
- 'type' => 'YML',
49
- 'source_lang' => 'en',
50
- 'source_file' => 'sample.yml',
51
- 'translation_file' => 'translations/<lang>/sample.yml'
52
- )
53
- end
54
-
55
- it "responds with not found when config can't be found" do
56
- message = 'Red alert!'
57
-
58
- expect(Txgh::Config::TxManager).to(
59
- receive(:tx_config).and_raise(Txgh::ConfigNotFoundError, message)
60
- )
61
-
62
- get '/config', project_slug: project_name
63
- expect(last_response.status).to eq(404)
64
- response = JSON.parse(last_response.body)
65
- expect(response).to eq([
66
- 'error' => message
67
- ])
68
- end
69
-
70
- it 'responds with internal error when an unexpected error occurs' do
71
- message = 'Red alert!'
72
-
73
- expect(Txgh::Config::TxManager).to(
74
- receive(:tx_config).and_raise(StandardError, message)
75
- )
76
-
77
- get '/config', project_slug: project_name
78
- expect(last_response.status).to eq(500)
79
- response = JSON.parse(last_response.body)
80
- expect(response).to eq([
81
- 'error' => message
82
- ])
83
- end
84
- end
85
- end
86
-
87
- describe Txgh::Hooks do
88
- include Rack::Test::Methods
89
- include StandardTxghSetup
90
- include Txgh::ResponseHelpers
91
-
92
- def app
93
- Txgh::Hooks
94
- end
95
-
96
- let(:config) do
97
- Txgh::Config::ConfigPair.new(project_config, repo_config)
98
- end
99
-
100
- before(:each) do
101
- allow(Txgh::Config::KeyManager).to(
102
- receive(:config_from_project).with(project_name).and_return(config)
103
- )
104
-
105
- allow(Txgh::Config::KeyManager).to(
106
- receive(:config_from_repo).with(repo_name).and_return(config)
107
- )
108
- end
109
-
110
- describe '/transifex' do
111
- def sign_with(body)
112
- header(
113
- Txgh::TransifexRequestAuth::TRANSIFEX_HEADER,
114
- Txgh::TransifexRequestAuth.header_value(
115
- body, config.transifex_project.webhook_secret
116
- )
117
- )
118
- end
119
-
120
- let(:handler) { double(:handler) }
121
-
122
- let(:params) do
123
- {
124
- 'project' => project_name,
125
- 'resource' => resource_slug,
126
- 'language' => language,
127
- 'translated' => '100'
128
- }
129
- end
130
-
131
- let(:payload) { URI.encode_www_form(params.to_a) }
132
-
133
- before(:each) do
134
- allow(Txgh::Handlers::Transifex::HookHandler).to(
135
- receive(:new) do |options|
136
- expect(options[:project].name).to eq(project_name)
137
- expect(options[:repo].name).to eq(repo_name)
138
- handler
139
- end
140
- )
141
- end
142
-
143
- it 'creates a handler and executes it' do
144
- expect(handler).to(
145
- receive(:execute).and_return(respond_with(200, true))
146
- )
147
-
148
- payload = URI.encode_www_form(params.to_a)
149
- sign_with payload
150
- post '/transifex', payload
151
- expect(last_response).to be_ok
152
- end
153
-
154
- it 'returns unauthorized if not properly signed' do
155
- post '/transifex', payload
156
- expect(last_response.status).to eq(401)
157
- end
158
-
159
- it 'returns internal error on unexpected error' do
160
- expect(Txgh::Config::KeyManager).to(
161
- receive(:config_from_project).and_raise(StandardError)
162
- )
163
-
164
- sign_with payload
165
- post '/transifex', payload
166
- expect(last_response.status).to eq(500)
167
- expect(JSON.parse(last_response.body)).to eq([
168
- 'error' => 'Internal server error: StandardError'
169
- ])
170
- end
171
- end
172
-
173
- describe '/github' do
174
- def sign_with(body)
175
- header(
176
- Txgh::GithubRequestAuth::GITHUB_HEADER,
177
- Txgh::GithubRequestAuth.header_value(
178
- body, config.github_repo.webhook_secret
179
- )
180
- )
181
- end
182
-
183
- describe 'push event' do
184
- let(:handler) { double(:handler) }
185
-
186
- before(:each) do
187
- allow(Txgh::Handlers::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)
191
- handler
192
- end
193
- )
194
- end
195
-
196
- it 'forwards the request to the github request handler' do
197
- expect(handler).to(
198
- receive(:execute).and_return(respond_with(200, true))
199
- )
200
-
201
- payload = GithubPayloadBuilder.push_payload(repo_name, ref)
202
-
203
- sign_with payload.to_json
204
- header 'X-GitHub-Event', 'push'
205
- post '/github', payload.to_json
206
-
207
- expect(last_response).to be_ok
208
- end
209
-
210
- it 'returns unauthorized if not properly signed' do
211
- payload = GithubPayloadBuilder.push_payload(repo_name, ref)
212
-
213
- header 'X-GitHub-Event', 'push'
214
- post '/github', payload.to_json
215
-
216
- expect(last_response.status).to eq(401)
217
- end
218
-
219
- it 'returns invalid request if event unrecognized' do
220
- header 'X-GitHub-Event', 'foobar'
221
- post '/github'
222
-
223
- expect(last_response.status).to eq(400)
224
- end
225
-
226
- it 'returns internal error on unexpected error' do
227
- payload = GithubPayloadBuilder.push_payload(repo_name, ref)
228
-
229
- expect(Txgh::Config::KeyManager).to(
230
- receive(:config_from_repo).and_raise(StandardError)
231
- )
232
-
233
- header 'X-GitHub-Event', 'push'
234
- post '/github', payload.to_json
235
-
236
- expect(last_response.status).to eq(500)
237
- expect(JSON.parse(last_response.body)).to eq([
238
- 'error' => 'Internal server error: StandardError'
239
- ])
240
- end
241
- end
242
- end
243
- end
244
-
245
- describe Txgh::Triggers do
246
- include Rack::Test::Methods
247
- include StandardTxghSetup
248
-
249
- def app
250
- Txgh::Triggers
251
- end
252
-
253
- let(:config) do
254
- Txgh::Config::ConfigPair.new(project_config, repo_config)
255
- end
256
-
257
- before(:each) do
258
- allow(Txgh::Config::KeyManager).to(
259
- receive(:config_from_project).with(project_name).and_return(config)
260
- )
261
-
262
- allow(Txgh::Config::KeyManager).to(
263
- receive(:config_from_repo).with(repo_name).and_return(config)
264
- )
265
- end
266
-
267
- describe '/push' do
268
- let(:params) do
269
- { project_slug: project_name, resource_slug: resource_slug, branch: branch }
270
- end
271
-
272
- it 'updates the expected resource' do
273
- updater = double(:updater)
274
- expect(Txgh::ResourceUpdater).to receive(:new).and_return(updater)
275
- expect(Txgh::GithubApi).to receive(:new).and_return(github_api)
276
- expect(github_api).to receive(:get_ref).and_return(object: { sha: 'abc123' })
277
-
278
- expect(updater).to receive(:update_resource) do |resource, sha|
279
- expected_branch = Txgh::Utils.absolute_branch(branch)
280
- expect(resource.branch).to eq(expected_branch)
281
- expect(resource.project_slug).to eq(project_name)
282
- expect(resource.resource_slug).to(
283
- eq("#{resource_slug}-#{Txgh::Utils.slugify(expected_branch)}")
284
- )
285
- end
286
-
287
- patch '/push', params
288
- expect(last_response).to be_ok
289
- end
290
-
291
- it 'returns internal error on unexpected error' do
292
- expect(Txgh::Config::KeyManager).to(
293
- receive(:config_from_project).and_raise(StandardError)
294
- )
295
-
296
- patch '/push', params
297
-
298
- expect(last_response.status).to eq(500)
299
- expect(JSON.parse(last_response.body)).to eq([
300
- { 'error' => 'Internal server error: StandardError' }
301
- ])
302
- end
303
- end
304
-
305
- describe '/pull' do
306
- let(:params) do
307
- { project_slug: project_name, resource_slug: resource_slug, branch: branch }
308
- end
309
-
310
- it 'updates translations (in all locales) in the expected repo' do
311
- committer = double(:committer)
312
- languages = [{ 'language_code' => 'pt' }, { 'language_code' => 'ja' }]
313
- project_config['languages'] = %w(pt ja)
314
- expect(Txgh::ResourceCommitter).to receive(:new).and_return(committer)
315
- expect(Txgh::TransifexApi).to receive(:new).and_return(transifex_api)
316
- expect(transifex_api).to receive(:get_languages).and_return(languages)
317
-
318
- languages.each do |language|
319
- expect(committer).to receive(:commit_resource) do |resource, branch, lang|
320
- expect(branch).to eq(branch)
321
- expect(lang).to eq(language['language_code'])
322
- expect(resource.branch).to eq(branch)
323
- expect(resource.project_slug).to eq(project_name)
324
- expect(resource.resource_slug).to(
325
- eq("#{resource_slug}-#{Txgh::Utils.slugify(branch)}")
326
- )
327
- end
328
- end
329
-
330
- patch '/pull', params
331
- expect(last_response).to be_ok
332
- end
333
-
334
- it 'returns internal error on unexpected error' do
335
- expect(Txgh::Config::KeyManager).to(
336
- receive(:config_from_project).and_raise(StandardError)
337
- )
338
-
339
- patch '/pull', params
340
-
341
- expect(last_response.status).to eq(500)
342
- expect(JSON.parse(last_response.body)).to eq([
343
- { 'error' => 'Internal server error: StandardError' }
344
- ])
345
- end
346
- end
347
- end
@@ -1,39 +0,0 @@
1
- require 'spec_helper'
2
- require 'rack'
3
-
4
- include Txgh
5
-
6
- describe GithubRequestAuth do
7
- let(:secret) { 'abc123' }
8
- let(:params) { '{"param1":"value1","param2":"value2","param3":123}' }
9
- let(:valid_signature) { 'ea62c3f65c8e42f155d96a25b7ba6eb5d320630e' }
10
-
11
- describe '.authentic_request?' do
12
- it 'returns true if the request is signed correctly' do
13
- request = Rack::Request.new(
14
- GithubRequestAuth::RACK_HEADER => "sha1=#{valid_signature}",
15
- 'rack.input' => StringIO.new(params)
16
- )
17
-
18
- authentic = GithubRequestAuth.authentic_request?(request, secret)
19
- expect(authentic).to eq(true)
20
- end
21
-
22
- it 'returns false if the request is not signed correctly' do
23
- request = Rack::Request.new(
24
- GithubRequestAuth::RACK_HEADER => 'incorrect',
25
- 'rack.input' => StringIO.new(params)
26
- )
27
-
28
- authentic = GithubRequestAuth.authentic_request?(request, secret)
29
- expect(authentic).to eq(false)
30
- end
31
- end
32
-
33
- describe '.header' do
34
- it 'calculates the signature and formats it as an http header' do
35
- value = GithubRequestAuth.header_value(params, secret)
36
- expect(value).to eq("sha1=#{valid_signature}")
37
- end
38
- end
39
- end