mail_fetcher 0.0.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b2b9c145d27321e349f33b17aa56c5dcc796c899
4
+ data.tar.gz: 01f673555ae9c88ba16fd3d1c304b1e180940489
5
+ SHA512:
6
+ metadata.gz: 4aa76e4a22aaa7b6f76825181b4f9330d5ca89e8b1799da2ef3a8ef13b1de1a9dfc1b4121d958f8dd5086f603e0f39eac01926f358b5a129fa279f3854ee50fe
7
+ data.tar.gz: df19f660f6d909c2758cae2540b4d893a9985139abc70b426c2ff4a54c9d482d93404bc0e63b21b647d016301308aebe317fd67b0956135d89b26e1060599e62
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .idea/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mail_fetcher.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Vitalii Grygoruk
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # MailFetcher
2
+
3
+ Simple API for accessing MailCatcher and GMail inbox. Useful for end-2-end test automation projects.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'mail_fetcher'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install mail_fetcher
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/vgrigoruk/mail_fetcher/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,5 @@
1
+ require "mail_fetcher/version"
2
+ require 'mail_fetcher/client'
3
+
4
+ module MailFetcher
5
+ end
@@ -0,0 +1,59 @@
1
+ require_relative 'gmail_client'
2
+ require_relative 'mail_catcher_client'
3
+ require 'yaml'
4
+
5
+ module MailFetcher
6
+ class Client
7
+ class << self
8
+ attr_accessor :server_type, :max_wait_time, :host, :port, :client_id, :client_secret, :account, :refresh_token, :clean_inbox, :logger
9
+
10
+ def configure
11
+ self.max_wait_time = 30
12
+ self.clean_inbox = false
13
+ yield self
14
+ if debug_mode && !self.logger
15
+ self.logger = self.create_default_logger
16
+ end
17
+ end
18
+
19
+ def find(*args)
20
+ client.find(*args)
21
+ end
22
+
23
+ protected
24
+
25
+ def client
26
+ @client ||= create_client
27
+ end
28
+
29
+ private
30
+
31
+ def create_client
32
+ case self.server_type
33
+ when :mail_catcher
34
+ _client = MailCatcherClient.new(host, port, clean_inbox = clean_inbox)
35
+ when :gmail
36
+ _client = GmailClient.new(account, client_id, client_secret, refresh_token)
37
+ else
38
+ raise InvalidArgument.new('Unsupported server type')
39
+ end
40
+ _client.logger = self.logger if self.logger
41
+ _client
42
+ end
43
+
44
+ def debug_mode
45
+ YAML.load(ENV.fetch('DEBUG', 'false'))
46
+ end
47
+
48
+ def create_default_logger
49
+ _logger = Logger.new(STDOUT)
50
+ _logger.level = Logger::DEBUG
51
+ _logger.formatter = proc do |severity, datetime, progname, msg|
52
+ "[#{severity}][#{datetime}] - #{msg}\n"
53
+ end
54
+ _logger.datetime_format = '%Y-%m-%d %H:%M:%S'
55
+ _logger
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,10 @@
1
+ module MailFetcher
2
+ class EmailMessage
3
+ URL_PATTERN = /https?:\/\/[_a-zA-Z0-9\.\/?=&-]+/
4
+
5
+ def initialize(connection, message_id)
6
+ @connection = connection
7
+ @message_id = message_id
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,66 @@
1
+ require 'mail'
2
+ require 'gmail_xoauth'
3
+ require 'faraday'
4
+ require_relative 'gmail_message'
5
+ require_relative 'retry_helper'
6
+
7
+ module MailFetcher
8
+ class GmailClient
9
+ include MailFetcher::RetryHelper
10
+
11
+ HOST = 'imap.gmail.com'
12
+ PORT = 993
13
+
14
+ attr_accessor :logger
15
+
16
+ def initialize(account, client_id, client_secret, refresh_token)
17
+ @account = account
18
+ @client_id = client_id
19
+ @client_secret = client_secret
20
+ @refresh_token = refresh_token
21
+ end
22
+
23
+ def find(recipient, subject='', wait=MailFetcher::Client.max_wait_time)
24
+ @connection ||= authenticated_connection
25
+
26
+ message_id = eventually(:tries => wait, :delay => 1) do
27
+ begin
28
+ @connection.examine('INBOX')
29
+ search_filter = ['TO', recipient, 'SUBJECT', subject]
30
+ results = @connection.search(search_filter)
31
+ logger.error("Inbox contains #{results.length} messages matching search criteria") if logger && results.length > 1
32
+ results.first
33
+ rescue => e
34
+ logger.error("Error while trying trying to find a message in INBOX (#{e.message})") if logger
35
+ nil
36
+ end
37
+ end
38
+
39
+ message_id ? GmailMessage.new(@connection, message_id) : nil
40
+ end
41
+
42
+ private
43
+
44
+ def authenticated_connection
45
+ connection = Net::IMAP.new(HOST, PORT, usessl = true, certs = nil, verify = false)
46
+ connection.authenticate('XOAUTH2', @account, get_access_token)
47
+ connection
48
+ end
49
+
50
+ def get_access_token
51
+ params = {}
52
+ params['client_id'] = @client_id
53
+ params['client_secret'] = @client_secret
54
+ params['refresh_token'] = @refresh_token
55
+ params['grant_type'] = 'refresh_token'
56
+ request_url = 'https://accounts.google.com'
57
+ conn = Faraday.new(:url => request_url) do |faraday|
58
+ faraday.request :url_encoded
59
+ faraday.adapter Faraday.default_adapter
60
+ end
61
+
62
+ response = conn.post('/o/oauth2/token', params)
63
+ JSON.parse(response.body)['access_token']
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,41 @@
1
+ require 'mail'
2
+ require_relative 'email_message'
3
+ module MailFetcher
4
+ class GmailMessage < EmailMessage
5
+ CONTENT_TYPE_PLAIN = /text\/plain/
6
+ CONTENT_TYPE_HTML = /text\/html/
7
+
8
+ def plain_text_body
9
+ find_part_by_content_type(CONTENT_TYPE_PLAIN).body.decoded
10
+ end
11
+
12
+ def plain_text_urls
13
+ plain_text_body.match(URL_PATTERN).to_a
14
+ end
15
+
16
+ def html_body
17
+ find_part_by_content_type(CONTENT_TYPE_HTML).body.decoded
18
+ end
19
+
20
+ def html_urls
21
+ html_body.scan(%r{href="(#{URL_PATTERN.source})"}).flatten
22
+ end
23
+
24
+ private
25
+
26
+ def find_part_by_content_type(content_type)
27
+ if email.content_type =~ content_type
28
+ email
29
+ else
30
+ email.parts.find { |p| p.content_type =~ content_type }
31
+ end
32
+ end
33
+
34
+ def email
35
+ @email ||= begin
36
+ raw_message = @connection.fetch(@message_id, 'RFC822.TEXT')[0].attr['RFC822.TEXT']
37
+ Mail.new(raw_message)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ require 'faraday_middleware'
2
+ require_relative 'retry_helper'
3
+ require_relative 'mail_catcher_message'
4
+
5
+ module MailFetcher
6
+ class MailCatcherClient
7
+
8
+ attr_accessor :logger
9
+
10
+ def initialize(host, port, clean_inbox=false)
11
+ base_url = "http://#{host}:#{port}"
12
+ @connection = Faraday.new base_url do |conn|
13
+ conn.request :json
14
+ conn.response :json, :content_type => /\bjson$/
15
+ conn.use :instrumentation
16
+ conn.adapter Faraday.default_adapter
17
+ end
18
+ delete_all_messages if clean_inbox
19
+ end
20
+
21
+ ## @return MailCatcherMessage if message found or nil
22
+ def find(recipient, subject='', wait=1)
23
+ message_id = eventually(:tries => wait, :delay => 1) do
24
+ message_data = all.find { |m| m['recipients'][0].include?(recipient) && m['subject'].include?(subject) }
25
+ message_data ? message_data['id'] : nil
26
+ end
27
+ message_id ? MailCatcherMessage.new(@connection, message_id) : nil
28
+ end
29
+
30
+ private
31
+
32
+ def delete_all_messages
33
+ @connection.delete('/messages')
34
+ end
35
+
36
+ def all
37
+ @connection.get('/messages').body
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'email_message'
2
+
3
+ module MailFetcher
4
+ class MailCatcherMessage < EmailMessage
5
+ def plain_text_body
6
+ body(:plain)
7
+ end
8
+
9
+ def plain_text_urls
10
+ plain_text_body.match(URL_PATTERN).to_a
11
+ end
12
+
13
+ def html_body
14
+ body(:html)
15
+ end
16
+
17
+ def html_urls
18
+ html_body.scan(%r{href="(#{URL_PATTERN.source})"}).flatten
19
+ end
20
+
21
+ private
22
+
23
+ ## @param format - :html, :plain, :json
24
+ def body(format=:json)
25
+ @connection.get("/messages/#{@message_id}.#{format}").body
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ module MailFetcher
2
+ module RetryHelper
3
+ def eventually(options = {})
4
+ options = {:tries => 10, :delay => 1, :catch => []}.merge(options)
5
+ last_error = nil
6
+ start_time = Time.now.to_i
7
+ options[:tries].times do |i|
8
+ begin
9
+ result = yield i
10
+ return result if result
11
+ rescue => e
12
+ raise e unless Array(options[:catch]).any? { |type| e.is_a?(type) }
13
+ last_error = e
14
+ end
15
+ timeout = options[:timeout] || (options[:tries] * options[:delay])
16
+ if (Time.now.to_i - start_time) > timeout
17
+ break
18
+ else
19
+ sleep(options[:delay])
20
+ end
21
+ end
22
+ raise last_error if last_error
23
+ nil
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module MailFetcher
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mail_fetcher/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mail_fetcher"
8
+ spec.version = MailFetcher::VERSION
9
+ spec.authors = ["Vitalii Grygoruk"]
10
+ spec.email = ["vitaliy[dot]grigoruk[at]gmail[dot]com"]
11
+ spec.summary = %q{Simple utility that allows to fetch email messages from Gmail and MailCatcher}
12
+ spec.description = %q{Find email messages in Gmail and MailCatcher using the same Ruby API. Extremely useful for acceptance testing, that relies on emails sent by application under test.}
13
+ spec.homepage = "https://github.com/vgrigoruk/mail_fetcher"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "rspec"
23
+ spec.add_development_dependency "mailcatcher"
24
+
25
+ spec.add_dependency "mail"
26
+ spec.add_dependency "gmail_xoauth"
27
+ spec.add_dependency "faraday"
28
+ spec.add_dependency "faraday_middleware"
29
+ end
@@ -0,0 +1,8 @@
1
+ require 'rspec'
2
+
3
+ describe 'Gmail client' do
4
+
5
+ it 'should find email message in Gmail' do
6
+
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ require 'rspec'
2
+
3
+ RSpec.configure do |config|
4
+
5
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mail_fetcher
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Vitalii Grygoruk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mailcatcher
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mail
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: gmail_xoauth
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: faraday
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: faraday_middleware
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Find email messages in Gmail and MailCatcher using the same Ruby API.
126
+ Extremely useful for acceptance testing, that relies on emails sent by application
127
+ under test.
128
+ email:
129
+ - vitaliy[dot]grigoruk[at]gmail[dot]com
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - ".gitignore"
135
+ - Gemfile
136
+ - LICENSE.txt
137
+ - README.md
138
+ - Rakefile
139
+ - lib/mail_fetcher.rb
140
+ - lib/mail_fetcher/client.rb
141
+ - lib/mail_fetcher/email_message.rb
142
+ - lib/mail_fetcher/gmail_client.rb
143
+ - lib/mail_fetcher/gmail_message.rb
144
+ - lib/mail_fetcher/mail_catcher_client.rb
145
+ - lib/mail_fetcher/mail_catcher_message.rb
146
+ - lib/mail_fetcher/retry_helper.rb
147
+ - lib/mail_fetcher/version.rb
148
+ - mail_fetcher.gemspec
149
+ - spec/features/gmail_spec.rb
150
+ - spec/spec_helper.rb
151
+ homepage: https://github.com/vgrigoruk/mail_fetcher
152
+ licenses:
153
+ - MIT
154
+ metadata: {}
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ requirements: []
170
+ rubyforge_project:
171
+ rubygems_version: 2.2.2
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: Simple utility that allows to fetch email messages from Gmail and MailCatcher
175
+ test_files:
176
+ - spec/features/gmail_spec.rb
177
+ - spec/spec_helper.rb