imap-backup 3.1.0 → 3.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a62efa6b4c2e63180629e4a9770cf6b636eb48fa1aaad0177d7bab975ce3f251
4
- data.tar.gz: ab0dee2784879df462e8fa1d622436b4a23f332dc731dd34ce353451a7164ce9
3
+ metadata.gz: b8661c03b8bf66e47c88fb69c476105945dad6fed867c510a151d62d0c401139
4
+ data.tar.gz: 8964a8ef14fd1f8b04d23745513b3bdb8ad8283d3875bd6d559ef00e5092d1f2
5
5
  SHA512:
6
- metadata.gz: ffe7443b8450980b05b170e8b4ed2d8cdaa093b17a60bf72c6c87c07e30752de1010aa444da3d4aea1b4348cbdd7b4fff0f2793e100f66de592796a8fa9885b6
7
- data.tar.gz: 5779c9d8bc98070cba06a68c571b849cdb33fb994954f6f3cf320642d1d49023ab9db5c6f713db26c413703caf046745589a61e5d4cde2e2c6bce751e7d6f3a1
6
+ metadata.gz: 8da502abc6b622f9261a223001a594e5e528d4f8871d209dd14a3bfd34693415bf5317b89d3bce892b0190027579d43f5c23f106f634423cb8fbf03fb82839e9
7
+ data.tar.gz: 610d08941b592e2cbdd29b46404f1e31f83bb31c5c83f57994cbef576ab9ec31b1f11d92851fec55810242661fc31cdb0fffe0816357cfe4177c29ba7c1d3134
@@ -2,6 +2,7 @@ require "net/imap"
2
2
  require "gmail_xoauth"
3
3
 
4
4
  require "gmail/authenticator"
5
+ require "retry_on_error"
5
6
 
6
7
  module Imap::Backup
7
8
  module Account; end
@@ -9,6 +10,10 @@ module Imap::Backup
9
10
  class Account::Connection
10
11
  class InvalidGmailOauth2RefreshToken < StandardError; end
11
12
 
13
+ include RetryOnError
14
+
15
+ LOGIN_RETRY_CLASSES = [EOFError, Errno::ECONNRESET, SocketError].freeze
16
+
12
17
  attr_reader :connection_options
13
18
  attr_reader :local_path
14
19
  attr_reader :password
@@ -79,26 +84,27 @@ module Imap::Backup
79
84
  end
80
85
 
81
86
  def imap
82
- return @imap unless @imap.nil?
83
-
84
- options = provider_options
85
- Imap::Backup.logger.debug(
86
- "Creating IMAP instance: #{server}, options: #{options.inspect}"
87
- )
88
- @imap = Net::IMAP.new(server, options)
89
- if gmail? && Gmail::Authenticator.refresh_token?(password)
90
- authenticator = Gmail::Authenticator.new(email: username, token: password)
91
- credentials = authenticator.credentials
92
- raise InvalidGmailOauth2RefreshToken if !credentials
93
-
94
- Imap::Backup.logger.debug "Logging in with OAuth2 token: #{username}"
95
- @imap.authenticate("XOAUTH2", username, credentials.access_token)
96
- else
97
- Imap::Backup.logger.debug "Logging in: #{username}/#{masked_password}"
98
- @imap.login(username, password)
99
- end
100
- Imap::Backup.logger.debug "Login complete"
101
- @imap
87
+ @imap ||=
88
+ retry_on_error(errors: LOGIN_RETRY_CLASSES) do
89
+ options = provider_options
90
+ Imap::Backup.logger.debug(
91
+ "Creating IMAP instance: #{server}, options: #{options.inspect}"
92
+ )
93
+ imap = Net::IMAP.new(server, options)
94
+ if gmail? && Gmail::Authenticator.refresh_token?(password)
95
+ authenticator = Gmail::Authenticator.new(email: username, token: password)
96
+ credentials = authenticator.credentials
97
+ raise InvalidGmailOauth2RefreshToken if !credentials
98
+
99
+ Imap::Backup.logger.debug "Logging in with OAuth2 token: #{username}"
100
+ imap.authenticate("XOAUTH2", username, credentials.access_token)
101
+ else
102
+ Imap::Backup.logger.debug "Logging in: #{username}/#{masked_password}"
103
+ imap.login(username, password)
104
+ end
105
+ Imap::Backup.logger.debug "Login complete"
106
+ imap
107
+ end
102
108
  end
103
109
 
104
110
  def server
@@ -175,7 +181,7 @@ module Imap::Backup
175
181
  def backup_folders
176
182
  @backup_folders ||=
177
183
  begin
178
- if @config_folders && @config_folders.any?
184
+ if @config_folders&.any?
179
185
  @config_folders
180
186
  else
181
187
  folders.map { |name| {name: name} }
@@ -1,5 +1,7 @@
1
1
  require "forwardable"
2
2
 
3
+ require "retry_on_error"
4
+
3
5
  module Imap::Backup
4
6
  module Account; end
5
7
 
@@ -7,8 +9,10 @@ module Imap::Backup
7
9
 
8
10
  class Account::Folder
9
11
  extend Forwardable
12
+ include RetryOnError
10
13
 
11
14
  REQUESTED_ATTRIBUTES = %w[RFC822 FLAGS INTERNALDATE].freeze
15
+ UID_FETCH_RETRY_CLASSES = [EOFError].freeze
12
16
 
13
17
  attr_reader :connection
14
18
  attr_reader :name
@@ -66,7 +70,10 @@ module Imap::Backup
66
70
 
67
71
  def fetch(uid)
68
72
  examine
69
- fetch_data_items = imap.uid_fetch([uid.to_i], REQUESTED_ATTRIBUTES)
73
+ fetch_data_items =
74
+ retry_on_error(errors: UID_FETCH_RETRY_CLASSES) do
75
+ imap.uid_fetch([uid.to_i], REQUESTED_ATTRIBUTES)
76
+ end
70
77
  return nil if fetch_data_items.nil?
71
78
 
72
79
  fetch_data_item = fetch_data_items[0]
@@ -51,18 +51,18 @@ module Imap::Backup
51
51
  private
52
52
 
53
53
  def data
54
- return @data if @data
55
-
56
- if File.exist?(pathname)
57
- Utils.check_permissions pathname, 0o600
58
- contents = File.read(pathname)
59
- @data = JSON.parse(contents, symbolize_names: true)
60
- else
61
- @data = {accounts: []}
62
- end
63
- @data[:debug] = false unless @data.include?(:debug)
64
- @data[:debug] = false unless [true, false].include?(@data[:debug])
65
- @data
54
+ @data ||=
55
+ begin
56
+ if File.exist?(pathname)
57
+ Utils.check_permissions pathname, 0o600
58
+ contents = File.read(pathname)
59
+ data = JSON.parse(contents, symbolize_names: true)
60
+ else
61
+ data = {accounts: []}
62
+ end
63
+ data[:debug] = data.key?(:debug) ? data[:debug] == true : false
64
+ data
65
+ end
66
66
  end
67
67
 
68
68
  def remove_modified_flags
@@ -161,7 +161,7 @@ module Imap::Backup
161
161
  def load_nth(index)
162
162
  enumerator = Serializer::MboxEnumerator.new(mbox_pathname)
163
163
  enumerator.each.with_index do |raw, i|
164
- next unless i == index
164
+ next if i != index
165
165
 
166
166
  return Email::Mboxrd::Message.from_serialized(raw)
167
167
  end
@@ -169,7 +169,7 @@ module Imap::Backup
169
169
  end
170
170
 
171
171
  def imap_looks_like_json?
172
- return false unless imap_exist?
172
+ return false if !imap_exist?
173
173
 
174
174
  content = File.read(imap_pathname)
175
175
  content.start_with?("{")
@@ -2,7 +2,7 @@ module Imap; end
2
2
 
3
3
  module Imap::Backup
4
4
  MAJOR = 3
5
- MINOR = 1
5
+ MINOR = 2
6
6
  REVISION = 0
7
7
  PRE = nil
8
8
  VERSION = [MAJOR, MINOR, REVISION, PRE].compact.map(&:to_s).join(".")
@@ -0,0 +1,14 @@
1
+ module RetryOnError
2
+ def retry_on_error(errors:, limit: 10)
3
+ tries ||= 1
4
+ yield
5
+ rescue *errors => e
6
+ if tries < limit
7
+ message = "#{e}, attempt #{tries} of #{limit}"
8
+ Imap::Backup.logger.debug message
9
+ tries += 1
10
+ retry
11
+ end
12
+ raise e
13
+ end
14
+ end
@@ -77,13 +77,15 @@ describe Imap::Backup::Account::Connection do
77
77
  end
78
78
 
79
79
  describe "#imap" do
80
- let!(:result) { subject.imap }
80
+ let(:result) { subject.imap }
81
81
 
82
82
  it "returns the IMAP connection" do
83
83
  expect(result).to eq(imap)
84
84
  end
85
85
 
86
86
  it "uses the password" do
87
+ result
88
+
87
89
  expect(imap).to have_received(:login).with(USERNAME, PASSWORD)
88
90
  end
89
91
 
@@ -138,7 +140,24 @@ describe Imap::Backup::Account::Connection do
138
140
  end
139
141
  end
140
142
 
141
- include_examples "connects to IMAP"
143
+ context "when the first login attempt fails" do
144
+ before do
145
+ outcomes = [-> { raise EOFError }, -> { true }]
146
+ allow(imap).to receive(:login) { outcomes.shift.call }
147
+ end
148
+
149
+ it "retries" do
150
+ subject.imap
151
+
152
+ expect(imap).to have_received(:login).twice
153
+ end
154
+ end
155
+
156
+ context "when run" do
157
+ before { subject.imap }
158
+
159
+ include_examples "connects to IMAP"
160
+ end
142
161
  end
143
162
 
144
163
  describe "#folders" do
@@ -1,8 +1,8 @@
1
1
  # rubocop:disable RSpec/PredicateMatcher
2
2
 
3
3
  describe Imap::Backup::Account::Folder do
4
- FOLDER_NAME = "Gelöscht"
5
- ENCODED_FOLDER_NAME = "Gel&APY-scht"
4
+ FOLDER_NAME = "Gelöscht".freeze
5
+ ENCODED_FOLDER_NAME = "Gel&APY-scht".freeze
6
6
 
7
7
  subject { described_class.new(connection, FOLDER_NAME) }
8
8
 
@@ -103,6 +103,23 @@ describe Imap::Backup::Account::Folder do
103
103
  expect(subject.fetch(123)).to be_nil
104
104
  end
105
105
  end
106
+
107
+ context "when the first fetch_uid attempts fail" do
108
+ before do
109
+ outcomes = [-> { raise EOFError }, -> { [fetch_data_item] }]
110
+ allow(imap).to receive(:uid_fetch) { outcomes.shift.call }
111
+ end
112
+
113
+ it "retries" do
114
+ subject.fetch(123)
115
+
116
+ expect(imap).to have_received(:uid_fetch).twice
117
+ end
118
+
119
+ it "succeeds" do
120
+ subject.fetch(123)
121
+ end
122
+ end
106
123
  end
107
124
 
108
125
  describe "#folder" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imap-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Yates
@@ -221,6 +221,7 @@ files:
221
221
  - lib/imap/backup/uploader.rb
222
222
  - lib/imap/backup/utils.rb
223
223
  - lib/imap/backup/version.rb
224
+ - lib/retry_on_error.rb
224
225
  - spec/features/backup_spec.rb
225
226
  - spec/features/helper.rb
226
227
  - spec/features/restore_spec.rb