txgh 5.3.4 → 5.4.0

Sign up to get free protection for your applications and to get access to all the features.
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