postageapp 1.2.5 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +10 -30
  4. data/{LICENSE → LICENSE.md} +1 -1
  5. data/README.md +118 -68
  6. data/Rakefile +15 -4
  7. data/VERSION +1 -0
  8. data/app/ingresses/action_mailbox/ingresses/postage_app.rb +2 -0
  9. data/app/ingresses/action_mailbox/ingresses/postage_app/inbound_emails_controller.rb +50 -0
  10. data/config/routes.rb +6 -0
  11. data/doc/RAILS2.md +54 -0
  12. data/doc/RAILS3.md +15 -0
  13. data/exe/postageapp +37 -0
  14. data/generators/postageapp/templates/postageapp_tasks.rake +25 -8
  15. data/lib/generators/postageapp/postageapp_generator.rb +9 -6
  16. data/lib/postageapp.rb +59 -30
  17. data/lib/postageapp/cli.rb +14 -0
  18. data/lib/postageapp/cli/command.rb +110 -0
  19. data/lib/postageapp/cli/command/config.rb +74 -0
  20. data/lib/postageapp/cli/command/create_mailbox.rb +21 -0
  21. data/lib/postageapp/cli/command/env.rb +58 -0
  22. data/lib/postageapp/cli/command/get_project_info.rb +3 -0
  23. data/lib/postageapp/configuration.rb +267 -63
  24. data/lib/postageapp/diagnostics.rb +30 -0
  25. data/lib/postageapp/engine.rb +9 -0
  26. data/lib/postageapp/env.rb +9 -0
  27. data/lib/postageapp/failed_request.rb +2 -0
  28. data/lib/postageapp/http.rb +32 -0
  29. data/lib/postageapp/logger.rb +2 -0
  30. data/lib/postageapp/mail.rb +1 -1
  31. data/lib/postageapp/mailer.rb +2 -11
  32. data/lib/postageapp/mailer/mailer_4.rb +29 -15
  33. data/lib/postageapp/rails/railtie.rb +1 -3
  34. data/lib/postageapp/request.rb +27 -11
  35. data/lib/postageapp/response.rb +22 -8
  36. data/lib/postageapp/utils.rb +0 -11
  37. data/log/.gitignore +1 -0
  38. data/postageapp.gemspec +16 -17
  39. data/script/with +3 -3
  40. data/test/gemfiles/Gemfile.rails-2.3.x +1 -1
  41. data/test/gemfiles/Gemfile.rails-3.0.x +1 -1
  42. data/test/gemfiles/Gemfile.rails-3.1.x +1 -1
  43. data/test/gemfiles/Gemfile.rails-3.2.x +1 -1
  44. data/test/gemfiles/Gemfile.rails-4.0.x +1 -1
  45. data/test/gemfiles/Gemfile.rails-4.1.x +1 -1
  46. data/test/gemfiles/Gemfile.rails-4.2.x +1 -2
  47. data/test/gemfiles/Gemfile.rails-5.0.x +12 -0
  48. data/test/gemfiles/Gemfile.rails-5.2.x +12 -0
  49. data/test/gemfiles/Gemfile.rails-6.0.x +12 -0
  50. data/test/gemfiles/Gemfile.rails-6.1.x +12 -0
  51. data/test/gemfiles/Gemfile.ruby +2 -3
  52. data/test/helper.rb +6 -17
  53. data/test/log/.gitignore +1 -0
  54. data/test/mailer/action_mailer_3/notifier.rb +10 -10
  55. data/test/tmp/.gitignore +1 -0
  56. data/test/travis_test.rb +58 -40
  57. data/test/{configuration_test.rb → unit/configuration_test.rb} +35 -22
  58. data/test/{failed_request_test.rb → unit/failed_request_test.rb} +6 -6
  59. data/test/{live_test.rb → unit/live_test.rb} +33 -43
  60. data/test/{mail_delivery_method_test.rb → unit/mail_delivery_method_test.rb} +1 -1
  61. data/test/{mailer_4_test.rb → unit/mailer_4_test.rb} +2 -2
  62. data/test/{mailer_helper_methods_test.rb → unit/mailer_helper_methods_test.rb} +4 -4
  63. data/test/{postageapp_test.rb → unit/postageapp_test.rb} +10 -1
  64. data/test/{rails_initialization_test.rb → unit/rails_initialization_test.rb} +2 -2
  65. data/test/{request_test.rb → unit/request_test.rb} +24 -23
  66. data/test/{response_test.rb → unit/response_test.rb} +4 -4
  67. data/test/unit/tmp/.gitignore +1 -0
  68. data/tmp/.gitignore +1 -0
  69. metadata +60 -68
  70. data/lib/postageapp/mailer/mailer_2.rb +0 -140
  71. data/lib/postageapp/mailer/mailer_3.rb +0 -190
  72. data/lib/postageapp/version.rb +0 -3
  73. data/test/mailer/action_mailer_2/notifier.rb +0 -76
  74. data/test/mailer/action_mailer_2/notifier/with_body_and_attachment.erb +0 -1
  75. data/test/mailer/action_mailer_2/notifier/with_custom_postage_variables.text.html.erb +0 -1
  76. data/test/mailer/action_mailer_2/notifier/with_custom_postage_variables.text.plain.erb +0 -1
  77. data/test/mailer/action_mailer_2/notifier/with_html_and_text_views.text.html.erb +0 -1
  78. data/test/mailer/action_mailer_2/notifier/with_html_and_text_views.text.plain.erb +0 -1
  79. data/test/mailer/action_mailer_2/notifier/with_simple_view.erb +0 -1
  80. data/test/mailer/action_mailer_2/notifier/with_text_only_view.text.plain.erb +0 -1
  81. data/test/mailer_2_test.rb +0 -95
  82. data/test/mailer_3_test.rb +0 -118
@@ -0,0 +1,74 @@
1
+ PostageApp::CLI::Command.define do
2
+ argument :'no-header',
3
+ optional: true,
4
+ boolean: true,
5
+ desc: 'An identifier to refer to this mailbox on subsequent API calls'
6
+
7
+ argument :markdown,
8
+ optional: true,
9
+ boolean: true,
10
+ desc: 'Emit markdown formatted description of variables'
11
+
12
+ perform do |arguments|
13
+ if (arguments[:markdown])
14
+ PostageApp::Configuration.params.each do |param, config|
15
+ case (default = config[:default])
16
+ when Proc
17
+ default = default.call
18
+ end
19
+
20
+ puts '* `%s`: %s (%s)' % [
21
+ param,
22
+ config[:desc],
23
+ case (config[:required])
24
+ when String
25
+ 'required %s' % config[:required]
26
+ when true
27
+ 'required'
28
+ else
29
+ default ? 'default: `%s`' % default : 'optional'
30
+ end
31
+ ]
32
+
33
+ config[:aliases]&.each do |param_alias|
34
+ puts '* `%s`: Alias for `%s`' % [
35
+ param_alias,
36
+ param
37
+ ]
38
+ end
39
+ end
40
+ else
41
+ unless (arguments[:'no-header'])
42
+ puts '%-40s %s' % [ 'Variable', 'Description' ]
43
+ puts '-' * 78
44
+ end
45
+
46
+ PostageApp::Configuration.params.each do |param, config|
47
+ case (default = config[:default])
48
+ when Proc
49
+ default = default.call
50
+ end
51
+
52
+ puts '%-40s %s (%s)' % [
53
+ param,
54
+ config[:desc],
55
+ case (config[:required])
56
+ when String
57
+ 'required %s' % config[:required]
58
+ when true
59
+ 'required'
60
+ else
61
+ default ? 'default: %s' % default : 'optional'
62
+ end
63
+ ]
64
+
65
+ config[:aliases]&.each do |param_alias|
66
+ puts '%-40s Alias for %s' % [
67
+ param_alias,
68
+ param
69
+ ]
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,21 @@
1
+ PostageApp::CLI::Command.define do
2
+ api_key :account
3
+
4
+ argument :uid,
5
+ optional: true,
6
+ desc: 'An identifier to refer to this mailbox on subsequent API calls'
7
+ argument :label,
8
+ optional: true,
9
+ desc: 'A descriptive name for this mailbox'
10
+ argument :host,
11
+ desc: 'IMAP server hostname'
12
+ argument :port,
13
+ optional: true,
14
+ desc: 'IMAP server port (default 993)'
15
+ argument :username,
16
+ desc: 'Username/email-address used to authenticate with the IMAP server'
17
+ argument :password,
18
+ desc: 'Password used to authenticate with the IMAP server'
19
+ argument :postback_url,
20
+ desc: 'The URL to post received email content to'
21
+ end
@@ -0,0 +1,58 @@
1
+ PostageApp::CLI::Command.define do
2
+ argument :'no-header',
3
+ optional: true,
4
+ boolean: true,
5
+ desc: 'Suppress display of header'
6
+
7
+ argument :markdown,
8
+ optional: true,
9
+ boolean: true,
10
+ desc: 'Emit markdown formatted description of variables'
11
+
12
+ perform do |arguments|
13
+ if (arguments[:markdown])
14
+ PostageApp::Configuration.params.each do |param, config|
15
+ config[:env_vars]&.each_with_index do |var, i|
16
+ case (i)
17
+ when 0
18
+ case (default = config[:default])
19
+ when Proc
20
+ default = default.call
21
+ end
22
+
23
+ puts '* `%s`: %s (%s)' % [
24
+ var,
25
+ config[:desc],
26
+ case (config[:required])
27
+ when String
28
+ 'required %s' % config[:required]
29
+ when true
30
+ 'required'
31
+ else
32
+ default ? 'default: `%s`' % default : 'optional'
33
+ end
34
+ ]
35
+ else
36
+ puts '* `%s`: Alias for `%s`' % [
37
+ var,
38
+ config[:env_vars][0]
39
+ ]
40
+ end
41
+ end
42
+ end
43
+ else
44
+ unless (arguments[:'no-header'])
45
+ puts '%-40s %s' % [ 'Variable', 'Setting' ]
46
+ puts '-' * 78
47
+ end
48
+
49
+ PostageApp::Configuration.params.each do |param, config|
50
+ config[:env_vars]&.each do |var|
51
+ puts '%-40s %s' % [
52
+ var, ENV[var]
53
+ ]
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ PostageApp::CLI::Command.define do
2
+ # No arguments required for this command
3
+ end
@@ -32,90 +32,294 @@
32
32
  # -------------
33
33
  # :proxy_host - Proxy server hostname
34
34
  # :proxy_port - Proxy server port
35
- # :proxy_user - Proxy server username
36
- # :proxy_pass - Proxy server password
35
+ # :proxy_username - Proxy server username
36
+ # :proxy_password - Proxy server password
37
37
 
38
38
  # Advanced Options
39
39
  # ----------------
40
40
  # :port - The port to make HTTP/HTTPS requests (default based on secure option)
41
- # :protocol - Set to either `http` or `https` (default based on secure option)
41
+ # :scheme - Set to either `http` or `https` (default based on secure option)
42
42
  # :requests_to_resend - List of API calls that should be replayed if they fail.
43
43
  # (default: send_message)
44
44
 
45
45
  class PostageApp::Configuration
46
- attr_accessor :secure
47
- attr_writer :protocol
48
- attr_accessor :host
49
- attr_writer :port
50
- attr_accessor :proxy_host
51
- attr_accessor :proxy_port
52
- attr_accessor :proxy_user
53
- attr_accessor :proxy_pass
54
- attr_accessor :http_open_timeout
55
- attr_accessor :http_read_timeout
56
- attr_accessor :recipient_override
57
- attr_accessor :requests_to_resend
58
- attr_accessor :project_root
59
- attr_accessor :framework
60
- attr_accessor :environment
61
- attr_accessor :logger
62
-
63
- def initialize
64
- @secure = true
65
- @host = 'api.postageapp.com'
46
+ # == Constants ============================================================
47
+
48
+ SOCKS5_PORT_DEFAULT = 1080
49
+ HTTP_PORT_DEFAULT = 80
50
+ HTTPS_PORT_DEFAULT = 443
51
+
52
+ SCHEME_FOR_SECURE = {
53
+ true => 'https'.freeze,
54
+ false => 'http'.freeze
55
+ }.freeze
56
+
57
+ CONFIG_PARAMS = {
58
+ api_key: {
59
+ default: nil,
60
+ desc: 'Project API key to use',
61
+ required: 'for project API functions'
62
+ },
63
+ account_api_key: {
64
+ default: nil,
65
+ desc: 'Account API key to use',
66
+ required: 'for account API functions'
67
+ },
68
+ postback_secret: {
69
+ default: nil,
70
+ desc: 'Secret to use for validating ActionMailbox requests'
71
+ },
72
+ project_root: {
73
+ default: -> {
74
+ if (defined?(Rails) and Rails.respond_to?(:root))
75
+ Rails.root
76
+ else
77
+ Dir.pwd
78
+ end
79
+ },
80
+ desc: 'Project root for logging purposes'
81
+ },
82
+ recipient_override: {
83
+ default: nil,
84
+ interrogator: true,
85
+ desc: 'Override sender on `send_message` calls'
86
+ },
87
+ logger: {
88
+ default: nil,
89
+ env: false,
90
+ desc: 'Logger instance to use'
91
+ },
92
+ secure: {
93
+ default: true,
94
+ interrogator: true,
95
+ env: false,
96
+ after_set: -> (config) {
97
+ if (config.secure?)
98
+ config.protocol = 'https'
99
+ if (config.port == 80)
100
+ config.port = 443
101
+ end
102
+ else
103
+ config.protocol = 'http'
104
+ if (config.port == 443)
105
+ config.port = 80
106
+ end
107
+ end
108
+ },
109
+ desc: 'Enable verifying TLS connections'
110
+ },
111
+ verify_tls: {
112
+ default: true,
113
+ aliases: [ :verify_certificate ],
114
+ interrogator: true,
115
+ parse: -> (v) {
116
+ case (v)
117
+ when 'true', 'yes', 'on'
118
+ true
119
+ when String
120
+ v.to_i != 0
121
+ else
122
+ !!v
123
+ end
124
+ },
125
+ desc: 'Enable TLS certificate verification'
126
+ },
127
+ host: {
128
+ default: 'api.postageapp.com'.freeze,
129
+ desc: 'API host to contact'
130
+ },
131
+ port: {
132
+ default: 443,
133
+ desc: 'API port to contact'
134
+ },
135
+ scheme: {
136
+ default: 'https'.freeze,
137
+ aliases: [ :protocol ],
138
+ desc: 'HTTP scheme to use'
139
+ },
140
+ proxy_username: {
141
+ default: nil,
142
+ aliases: [ :proxy_user ],
143
+ desc: 'SOCKS5 proxy username'
144
+ },
145
+ proxy_password: {
146
+ default: nil,
147
+ aliases: [ :proxy_pass ],
148
+ desc: 'SOCKS5 proxy password'
149
+ },
150
+ proxy_host: {
151
+ default: nil,
152
+ desc: 'SOCKS5 proxy host'
153
+ },
154
+ proxy_port: {
155
+ default: 1080,
156
+ parse: -> (v) { v.to_i },
157
+ desc: 'SOCKS5 proxy port'
158
+ },
159
+ open_timeout: {
160
+ default: 5,
161
+ aliases: [ :http_open_timeout ],
162
+ parse: -> (v) { v.to_i },
163
+ desc: 'Timeout in seconds when initiating requests'
164
+ },
165
+ read_timeout: {
166
+ default: 10,
167
+ aliases: [ :http_read_timeout ],
168
+ parse: -> (v) { v.to_i },
169
+ desc: 'Timeout in seconds when awaiting responses'
170
+ },
171
+ retry_methods: {
172
+ default: %w[ send_message ].freeze,
173
+ aliases: [ :requests_to_resend ],
174
+ parse: -> (v) {
175
+ case (v)
176
+ when String
177
+ v.split(/\s*(?:,|\s)\s*/).grep(/\S/)
178
+ else
179
+ v
180
+ end
181
+ },
182
+ desc: 'Which API calls to retry, comma and/or space separated'
183
+ },
184
+ framework: {
185
+ default: -> {
186
+ if (defined?(Rails) and Rails.respond_to?(:version))
187
+ 'Ruby %s / Ruby on Rails %s' % [
188
+ RUBY_VERSION,
189
+ Rails.version
190
+ ]
191
+ else
192
+ 'Ruby %s' % RUBY_VERSION
193
+ end
194
+ },
195
+ desc: 'Framework used'
196
+ },
197
+ environment: {
198
+ default: 'production',
199
+ desc: 'Environment to use'
200
+ }
201
+ }.freeze
202
+
203
+ # == Properties ===========================================================
204
+
205
+ CONFIG_PARAMS.each do |param, config|
206
+ attr_reader param
207
+
208
+ ivar = config[:ivar] ||= :"@#{param}"
209
+ mutator_method = :"#{param}="
210
+ config[:sources] = [ param ]
211
+ after_set = config[:after_set]
212
+
213
+ if (parser = config[:parse])
214
+ define_method(mutator_method) do |v|
215
+ instance_variable_set(ivar, parser.call(v))
216
+ end
217
+ else
218
+ define_method(mutator_method) do |v|
219
+ instance_variable_set(ivar, v)
220
+
221
+ after_set and after_set[self]
222
+ end
223
+ end
224
+
225
+ interrogator_method = nil
226
+
227
+ if (config[:interrogator])
228
+ interrogator_method = :"#{param}?"
229
+ define_method(interrogator_method) do
230
+ !!instance_variable_get(ivar)
231
+ end
232
+ end
233
+
234
+ if (param_aliases = config[:aliases])
235
+ param_aliases.each do |param_alias|
236
+ config[:sources] << param_alias
66
237
 
67
- @http_open_timeout = 5
68
- @http_read_timeout = 10
238
+ alias_method param_alias, param
239
+ alias_method :"#{param_alias}=", mutator_method
69
240
 
70
- @requests_to_resend = %w[ send_message ]
241
+ if (config[:interrogator])
242
+ alias_method :"#{param_alias}?", interrogator_method
243
+ end
244
+ end
245
+ end
71
246
 
72
- @framework = 'Ruby'
247
+ unless (config[:env] === false)
248
+ config[:env_vars] = config[:sources].map do |source|
249
+ 'POSTAGEAPP_' + source.to_s.upcase
250
+ end
251
+ end
73
252
 
74
- @environment = 'production'
253
+ # config[:getters] = config[:sources].map do |source|
254
+ # -> (credentials) { credentials[:source] }
255
+ # end + config[:env_vars].map do |var|
256
+ # -> (_) { ENV[var] }
257
+ # end
75
258
  end
76
-
77
- alias_method :secure?, :secure
78
-
79
- # Assign which API key is used to make API calls. Can also be specified
80
- # using the `POSTAGEAPP_API_KEY` environment variable.
81
- def api_key=(key)
82
- @api_key = key
259
+
260
+ # == Class Methods ========================================================
261
+
262
+ def self.params
263
+ CONFIG_PARAMS
83
264
  end
84
-
85
- # Returns the API key used to make API calls. Can be specified as the
86
- # `POSTAGEAPP_API_KEY` environment variable.
87
- def api_key
88
- @api_key ||= ENV['POSTAGEAPP_API_KEY']
265
+
266
+ # == Instance Methods =====================================================
267
+
268
+ def initialize
269
+ credentials = self.rails_credentials
270
+
271
+ CONFIG_PARAMS.each do |param, config|
272
+ value = (
273
+ config[:sources]&.map { |s| credentials[s] }&.compact&.first ||
274
+ config[:env_vars]&.map { |v| ENV[v] }&.compact&.first
275
+ )
276
+
277
+ if (value)
278
+ if (config[:parse])
279
+ instance_variable_set(config[:ivar], config[:parse].call(value))
280
+ else
281
+ instance_variable_set(config[:ivar], value)
282
+ end
283
+ else
284
+ case (config[:default])
285
+ when Proc
286
+ instance_variable_set(config[:ivar], config[:default].call)
287
+ else
288
+ instance_variable_set(config[:ivar], config[:default])
289
+ end
290
+ end
291
+ end
89
292
  end
90
-
91
- # Returns the HTTP protocol used to make API calls
92
- def protocol
93
- @protocol ||= (secure? ? 'https' : 'http')
293
+
294
+ # Returns true if the port used for the API is the default port, otherwise
295
+ # false. 80 for HTTP, 443 for HTTPS.
296
+ def port_default?
297
+ self.port == (self.secure? ? HTTPS_PORT_DEFAULT : HTTP_PORT_DEFAULT)
94
298
  end
95
-
96
- # Returns the port used to make API calls
97
- def port
98
- @port ||= (secure? ? 443 : 80)
299
+
300
+ # Returns true if a proxy is defined, otherwise false.
301
+ def proxy?
302
+ self.proxy_host and self.proxy_host.match(/\A\S+\z/)
99
303
  end
100
-
304
+
101
305
  # Returns the endpoint URL to make API calls
102
306
  def url
103
- "#{self.protocol}://#{self.host}:#{self.port}"
307
+ '%s://%s%s' % [
308
+ self.scheme,
309
+ self.host,
310
+ self.port_default? ? '' : (':%d' % self.port)
311
+ ]
104
312
  end
105
313
 
106
- # Returns a properly configured Net::HTTP connection
314
+ # Returns a connection aimed at the API endpoint
107
315
  def http
108
- http = Net::HTTP::Proxy(
109
- self.proxy_host,
110
- self.proxy_port,
111
- self.proxy_user,
112
- self.proxy_pass
113
- ).new(self.host, self.port)
114
-
115
- http.read_timeout = self.http_read_timeout
116
- http.open_timeout = self.http_open_timeout
117
- http.use_ssl = self.secure?
118
-
119
- http
316
+ PostageApp::HTTP.connect(self)
317
+ end
318
+
319
+ protected
320
+ def rails_credentials
321
+ if (PostageApp::Env.rails_with_encrypted_credentials?)
322
+ Rails.application.credentials.postageapp
323
+ end or { }
120
324
  end
121
325
  end