gitlab-mail_room 0.0.7 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +7 -7
  3. data/.rubocop.yml +5 -0
  4. data/.rubocop_todo.yml +501 -0
  5. data/.ruby-version +1 -1
  6. data/.travis.yml +12 -5
  7. data/README.md +104 -10
  8. data/Rakefile +1 -1
  9. data/lib/mail_room.rb +2 -0
  10. data/lib/mail_room/arbitration/redis.rb +1 -1
  11. data/lib/mail_room/cli.rb +1 -1
  12. data/lib/mail_room/configuration.rb +11 -1
  13. data/lib/mail_room/connection.rb +6 -182
  14. data/lib/mail_room/coordinator.rb +8 -4
  15. data/lib/mail_room/crash_handler.rb +2 -2
  16. data/lib/mail_room/delivery/letter_opener.rb +1 -1
  17. data/lib/mail_room/health_check.rb +60 -0
  18. data/lib/mail_room/imap.rb +8 -0
  19. data/lib/mail_room/imap/connection.rb +200 -0
  20. data/lib/mail_room/imap/message.rb +19 -0
  21. data/lib/mail_room/logger/structured.rb +15 -1
  22. data/lib/mail_room/mailbox.rb +61 -21
  23. data/lib/mail_room/mailbox_watcher.rb +15 -2
  24. data/lib/mail_room/message.rb +16 -0
  25. data/lib/mail_room/microsoft_graph.rb +7 -0
  26. data/lib/mail_room/microsoft_graph/connection.rb +217 -0
  27. data/lib/mail_room/version.rb +1 -1
  28. data/mail_room.gemspec +6 -0
  29. data/spec/fixtures/test_config.yml +3 -0
  30. data/spec/lib/cli_spec.rb +6 -6
  31. data/spec/lib/configuration_spec.rb +10 -2
  32. data/spec/lib/coordinator_spec.rb +16 -2
  33. data/spec/lib/delivery/letter_opener_spec.rb +2 -2
  34. data/spec/lib/delivery/logger_spec.rb +1 -1
  35. data/spec/lib/delivery/postback_spec.rb +11 -11
  36. data/spec/lib/health_check_spec.rb +57 -0
  37. data/spec/lib/{connection_spec.rb → imap/connection_spec.rb} +12 -8
  38. data/spec/lib/imap/message_spec.rb +36 -0
  39. data/spec/lib/logger/structured_spec.rb +34 -2
  40. data/spec/lib/mailbox_spec.rb +75 -27
  41. data/spec/lib/mailbox_watcher_spec.rb +54 -38
  42. data/spec/lib/message_spec.rb +35 -0
  43. data/spec/lib/microsoft_graph/connection_spec.rb +190 -0
  44. data/spec/spec_helper.rb +14 -3
  45. metadata +92 -5
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal:true
2
+
3
+ require 'spec_helper'
4
+ require 'securerandom'
5
+
6
+ describe MailRoom::Message do
7
+ let(:uid) { SecureRandom.hex }
8
+ let(:body) { 'hello world' }
9
+
10
+ subject { described_class.new(uid: uid, body: body) }
11
+
12
+ describe '#initalize' do
13
+ it 'initializes with required parameters' do
14
+ subject
15
+
16
+ expect(subject.uid).to eq(uid)
17
+ expect(subject.body).to eq(body)
18
+ end
19
+ end
20
+
21
+ describe '#==' do
22
+ let(:dup) { described_class.new(uid: uid, body: body) }
23
+
24
+ it 'matches an equivalent message' do
25
+ expect(dup == subject).to be true
26
+ end
27
+
28
+ it 'does not match a message with a different UID' do
29
+ msg = described_class.new(uid: '12345', body: body)
30
+
31
+ expect(subject == msg).to be false
32
+ expect(msg == subject).to be false
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'spec_helper'
5
+ require 'json'
6
+ require 'webmock/rspec'
7
+
8
+ describe MailRoom::MicrosoftGraph::Connection do
9
+ let(:tenant_id) { options[:inbox_options][:tenant_id] }
10
+ let(:options) do
11
+ {
12
+ delete_after_delivery: true,
13
+ expunge_deleted: true
14
+ }.merge(REQUIRED_MICROSOFT_GRAPH_DEFAULTS)
15
+ end
16
+ let(:mailbox) { build_mailbox(options) }
17
+ let(:base_url) { 'https://graph.microsoft.com/v1.0/users/user@example.com/mailFolders/inbox/messages' }
18
+ let(:message_base_url) { 'https://graph.microsoft.com/v1.0/users/user@example.com/messages' }
19
+
20
+ before do
21
+ WebMock.enable!
22
+ end
23
+
24
+ context '#wait' do
25
+ let(:connection) { described_class.new(mailbox) }
26
+ let(:uid) { 1 }
27
+ let(:access_token) { SecureRandom.hex }
28
+ let(:refresh_token) { SecureRandom.hex }
29
+ let(:expires_in) { Time.now + 3600 }
30
+ let(:unread_messages_body) { '' }
31
+ let(:status) { 200 }
32
+ let!(:stub_token) do
33
+ stub_request(:post, "https://login.microsoftonline.com/#{tenant_id}/oauth2/v2.0/token").to_return(
34
+ body: { 'access_token' => access_token, 'refresh_token' => refresh_token, 'expires_in' => expires_in }.to_json,
35
+ headers: { 'Content-Type' => 'application/json' }
36
+ )
37
+ end
38
+ let!(:stub_unread_messages_request) do
39
+ stub_request(:get, "#{base_url}?$filter=isRead%20eq%20false").to_return(
40
+ status: status,
41
+ body: unread_messages_body.to_json,
42
+ headers: { 'Content-Type' => 'application/json' }
43
+ )
44
+ end
45
+
46
+ before do
47
+ connection.stubs(:wait_for_new_messages)
48
+ end
49
+
50
+ describe 'poll interval' do
51
+ it 'defaults to 60 seconds' do
52
+ expect(connection.send(:poll_interval)).to eq(60)
53
+ end
54
+
55
+ context 'interval set to 10' do
56
+ let(:options) do
57
+ {
58
+ inbox_method: :microsoft_graph,
59
+ inbox_options: {
60
+ tenant_id: '98776',
61
+ client_id: '12345',
62
+ client_secret: 'MY-SECRET',
63
+ poll_interval: '10'
64
+ }
65
+ }
66
+ end
67
+
68
+ it 'sets the poll interval to 10' do
69
+ expect(connection.send(:poll_interval)).to eq(10)
70
+ end
71
+ end
72
+ end
73
+
74
+ context 'with a single message' do
75
+ let(:message_id) { SecureRandom.hex }
76
+ let(:unread_messages_body) { { value: ['id' => message_id] } }
77
+ let(:message_url) { "#{message_base_url}/#{message_id}" }
78
+ let(:message_body) { 'hello world' }
79
+
80
+ it 'requests message ID' do
81
+ stub_get = stub_request(:get, "#{message_url}/$value").to_return(
82
+ status: 200,
83
+ body: message_body
84
+ )
85
+ stub_patch = stub_request(:patch, message_url).with(body: { "isRead": true }.to_json)
86
+ stub_delete = stub_request(:delete, message_url)
87
+ message_count = 0
88
+
89
+ connection.on_new_message do |message|
90
+ message_count += 1
91
+ expect(message.uid).to eq(message_id)
92
+ expect(message.body).to eq(message_body)
93
+ end
94
+
95
+ connection.wait
96
+
97
+ assert_requested(stub_token)
98
+ assert_requested(stub_unread_messages_request)
99
+ assert_requested(stub_get)
100
+ assert_requested(stub_patch)
101
+ assert_requested(stub_delete)
102
+ expect(message_count).to eq(1)
103
+ end
104
+ end
105
+
106
+ context 'with multiple pages of messages' do
107
+ let(:message_ids) { [SecureRandom.hex, SecureRandom.hex] }
108
+ let(:next_page_url) { 'https://graph.microsoft.com/v1.0/nextPage' }
109
+ let(:unread_messages_body) { { value: ['id' => message_ids.first], '@odata.nextLink' => next_page_url } }
110
+ let(:message_body) { 'hello world' }
111
+
112
+ it 'requests message ID' do
113
+ stub_request(:get, next_page_url).to_return(
114
+ status: 200,
115
+ body: { value: ['id' => message_ids[1]] }.to_json
116
+ )
117
+
118
+ stubs = []
119
+ message_ids.each do |message_id|
120
+ rfc822_msg_url = "#{message_base_url}/#{message_id}/$value"
121
+ stubs << stub_request(:get, rfc822_msg_url).to_return(
122
+ status: 200,
123
+ body: message_body
124
+ )
125
+
126
+ msg_url = "#{message_base_url}/#{message_id}"
127
+ stubs << stub_request(:patch, msg_url).with(body: { "isRead": true }.to_json)
128
+ stubs << stub_request(:delete, msg_url)
129
+ end
130
+
131
+ message_count = 0
132
+
133
+ connection.on_new_message do |message|
134
+ expect(message.uid).to eq(message_ids[message_count])
135
+ expect(message.body).to eq(message_body)
136
+ message_count += 1
137
+ end
138
+
139
+ connection.wait
140
+
141
+ stubs.each { |stub| assert_requested(stub) }
142
+ expect(message_count).to eq(2)
143
+ end
144
+ end
145
+
146
+ shared_examples 'request backoff' do
147
+ it 'backs off' do
148
+ connection.expects(:backoff)
149
+
150
+ connection.on_new_message {}
151
+ connection.wait
152
+
153
+ expect(connection.throttled_count).to eq(1)
154
+ end
155
+ end
156
+
157
+ context 'too many requests' do
158
+ let(:status) { 429 }
159
+
160
+ it_behaves_like 'request backoff'
161
+ end
162
+
163
+ context 'too much bandwidth' do
164
+ let(:status) { 509 }
165
+
166
+ it_behaves_like 'request backoff'
167
+ end
168
+
169
+ context 'invalid JSON response' do
170
+ let(:body) { 'this is something' }
171
+
172
+ it 'ignores the message and logs a warning' do
173
+ mailbox.logger.expects(:warn)
174
+
175
+ connection.on_new_message {}
176
+ connection.wait
177
+ end
178
+ end
179
+
180
+ context '500 error' do
181
+ let(:status) { 500 }
182
+
183
+ it 'terminates due to error' do
184
+ connection.on_new_message {}
185
+
186
+ expect { connection.wait }.to raise_error(OAuth2::Error)
187
+ end
188
+ end
189
+ end
190
+ end
data/spec/spec_helper.rb CHANGED
@@ -2,6 +2,7 @@ require 'simplecov'
2
2
  SimpleCov.start
3
3
 
4
4
  require 'bundler/setup'
5
+ require 'date'
5
6
 
6
7
  require 'rspec'
7
8
  require 'mocha/api'
@@ -21,11 +22,21 @@ RSpec.configure do |config|
21
22
  end
22
23
 
23
24
  REQUIRED_MAILBOX_DEFAULTS = {
24
- :name => "inbox",
25
- :email => "user@example.com",
26
- :password => "password123"
25
+ name: "inbox",
26
+ email: "user@example.com",
27
+ password: "password123"
27
28
  }
28
29
 
30
+ REQUIRED_MICROSOFT_GRAPH_DEFAULTS = {
31
+ password: nil,
32
+ inbox_method: :microsoft_graph,
33
+ inbox_options: {
34
+ tenant_id: '98776',
35
+ client_id: '12345',
36
+ client_secret: 'MY-SECRET',
37
+ }.freeze
38
+ }.freeze
39
+
29
40
  def build_mailbox(options = {})
30
41
  MailRoom::Mailbox.new(REQUIRED_MAILBOX_DEFAULTS.merge(options))
31
42
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-mail_room
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Pitale
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-12 00:00:00.000000000 Z
11
+ date: 2021-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: net-imap
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: oauth2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.4.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.4.4
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: rake
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +66,20 @@ dependencies:
38
66
  - - "~>"
39
67
  - !ruby/object:Gem::Version
40
68
  version: '3.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.11'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.11'
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: mocha
43
85
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +108,20 @@ dependencies:
66
108
  - - ">="
67
109
  - !ruby/object:Gem::Version
68
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: webrick
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.6'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.6'
69
125
  - !ruby/object:Gem::Dependency
70
126
  name: faraday
71
127
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +220,20 @@ dependencies:
164
220
  - - ">="
165
221
  - !ruby/object:Gem::Version
166
222
  version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: webmock
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
167
237
  description: mail_room will proxy email (gmail) from IMAP to a delivery method
168
238
  email:
169
239
  - tpitale@gmail.com
@@ -175,6 +245,8 @@ files:
175
245
  - ".gitignore"
176
246
  - ".gitlab-ci.yml"
177
247
  - ".gitlab/issue_templates/Release.md"
248
+ - ".rubocop.yml"
249
+ - ".rubocop_todo.yml"
178
250
  - ".ruby-version"
179
251
  - ".travis.yml"
180
252
  - CHANGELOG.md
@@ -200,9 +272,16 @@ files:
200
272
  - lib/mail_room/delivery/postback.rb
201
273
  - lib/mail_room/delivery/que.rb
202
274
  - lib/mail_room/delivery/sidekiq.rb
275
+ - lib/mail_room/health_check.rb
276
+ - lib/mail_room/imap.rb
277
+ - lib/mail_room/imap/connection.rb
278
+ - lib/mail_room/imap/message.rb
203
279
  - lib/mail_room/logger/structured.rb
204
280
  - lib/mail_room/mailbox.rb
205
281
  - lib/mail_room/mailbox_watcher.rb
282
+ - lib/mail_room/message.rb
283
+ - lib/mail_room/microsoft_graph.rb
284
+ - lib/mail_room/microsoft_graph/connection.rb
206
285
  - lib/mail_room/version.rb
207
286
  - logfile.log
208
287
  - mail_room.gemspec
@@ -210,7 +289,6 @@ files:
210
289
  - spec/lib/arbitration/redis_spec.rb
211
290
  - spec/lib/cli_spec.rb
212
291
  - spec/lib/configuration_spec.rb
213
- - spec/lib/connection_spec.rb
214
292
  - spec/lib/coordinator_spec.rb
215
293
  - spec/lib/crash_handler_spec.rb
216
294
  - spec/lib/delivery/letter_opener_spec.rb
@@ -218,9 +296,14 @@ files:
218
296
  - spec/lib/delivery/postback_spec.rb
219
297
  - spec/lib/delivery/que_spec.rb
220
298
  - spec/lib/delivery/sidekiq_spec.rb
299
+ - spec/lib/health_check_spec.rb
300
+ - spec/lib/imap/connection_spec.rb
301
+ - spec/lib/imap/message_spec.rb
221
302
  - spec/lib/logger/structured_spec.rb
222
303
  - spec/lib/mailbox_spec.rb
223
304
  - spec/lib/mailbox_watcher_spec.rb
305
+ - spec/lib/message_spec.rb
306
+ - spec/lib/microsoft_graph/connection_spec.rb
224
307
  - spec/spec_helper.rb
225
308
  homepage: http://github.com/tpitale/mail_room
226
309
  licenses: []
@@ -240,7 +323,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
240
323
  - !ruby/object:Gem::Version
241
324
  version: '0'
242
325
  requirements: []
243
- rubygems_version: 3.0.1
326
+ rubygems_version: 3.1.4
244
327
  signing_key:
245
328
  specification_version: 4
246
329
  summary: mail_room will proxy email (gmail) from IMAP to a callback URL, logger, or
@@ -250,7 +333,6 @@ test_files:
250
333
  - spec/lib/arbitration/redis_spec.rb
251
334
  - spec/lib/cli_spec.rb
252
335
  - spec/lib/configuration_spec.rb
253
- - spec/lib/connection_spec.rb
254
336
  - spec/lib/coordinator_spec.rb
255
337
  - spec/lib/crash_handler_spec.rb
256
338
  - spec/lib/delivery/letter_opener_spec.rb
@@ -258,7 +340,12 @@ test_files:
258
340
  - spec/lib/delivery/postback_spec.rb
259
341
  - spec/lib/delivery/que_spec.rb
260
342
  - spec/lib/delivery/sidekiq_spec.rb
343
+ - spec/lib/health_check_spec.rb
344
+ - spec/lib/imap/connection_spec.rb
345
+ - spec/lib/imap/message_spec.rb
261
346
  - spec/lib/logger/structured_spec.rb
262
347
  - spec/lib/mailbox_spec.rb
263
348
  - spec/lib/mailbox_watcher_spec.rb
349
+ - spec/lib/message_spec.rb
350
+ - spec/lib/microsoft_graph/connection_spec.rb
264
351
  - spec/spec_helper.rb