gitlab-mail_room 0.0.7 → 0.0.12

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 (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