actionmailbox 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/Gemfile +8 -0
  4. data/Gemfile.lock +159 -0
  5. data/LICENSE +21 -0
  6. data/README.md +278 -0
  7. data/Rakefile +27 -0
  8. data/actionmailbox.gemspec +27 -0
  9. data/app/controllers/action_mailbox/base_controller.rb +43 -0
  10. data/app/controllers/action_mailbox/ingresses/amazon/inbound_emails_controller.rb +50 -0
  11. data/app/controllers/action_mailbox/ingresses/mailgun/inbound_emails_controller.rb +99 -0
  12. data/app/controllers/action_mailbox/ingresses/mandrill/inbound_emails_controller.rb +78 -0
  13. data/app/controllers/action_mailbox/ingresses/postfix/inbound_emails_controller.rb +55 -0
  14. data/app/controllers/action_mailbox/ingresses/sendgrid/inbound_emails_controller.rb +50 -0
  15. data/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb +27 -0
  16. data/app/controllers/rails/conductor/action_mailbox/reroutes_controller.rb +15 -0
  17. data/app/controllers/rails/conductor/base_controller.rb +10 -0
  18. data/app/jobs/action_mailbox/incineration_job.rb +18 -0
  19. data/app/jobs/action_mailbox/routing_job.rb +9 -0
  20. data/app/models/action_mailbox/inbound_email.rb +43 -0
  21. data/app/models/action_mailbox/inbound_email/incineratable.rb +18 -0
  22. data/app/models/action_mailbox/inbound_email/incineratable/incineration.rb +22 -0
  23. data/app/models/action_mailbox/inbound_email/message_id.rb +36 -0
  24. data/app/models/action_mailbox/inbound_email/routable.rb +22 -0
  25. data/app/views/layouts/rails/conductor.html.erb +7 -0
  26. data/app/views/rails/conductor/action_mailbox/inbound_emails/index.html.erb +15 -0
  27. data/app/views/rails/conductor/action_mailbox/inbound_emails/new.html.erb +42 -0
  28. data/app/views/rails/conductor/action_mailbox/inbound_emails/show.html.erb +15 -0
  29. data/bin/test +5 -0
  30. data/config/routes.rb +19 -0
  31. data/db/migrate/20180917164000_create_action_mailbox_tables.rb +11 -0
  32. data/lib/action_mailbox.rb +15 -0
  33. data/lib/action_mailbox/base.rb +111 -0
  34. data/lib/action_mailbox/callbacks.rb +32 -0
  35. data/lib/action_mailbox/engine.rb +34 -0
  36. data/lib/action_mailbox/mail_ext.rb +4 -0
  37. data/lib/action_mailbox/mail_ext/address_equality.rb +5 -0
  38. data/lib/action_mailbox/mail_ext/address_wrapping.rb +5 -0
  39. data/lib/action_mailbox/mail_ext/addresses.rb +25 -0
  40. data/lib/action_mailbox/mail_ext/from_source.rb +5 -0
  41. data/lib/action_mailbox/mail_ext/recipients.rb +5 -0
  42. data/lib/action_mailbox/postfix_relayer.rb +67 -0
  43. data/lib/action_mailbox/router.rb +38 -0
  44. data/lib/action_mailbox/router/route.rb +38 -0
  45. data/lib/action_mailbox/routing.rb +20 -0
  46. data/lib/action_mailbox/test_case.rb +8 -0
  47. data/lib/action_mailbox/test_helper.rb +42 -0
  48. data/lib/action_mailbox/version.rb +3 -0
  49. data/lib/tasks/ingress.rake +24 -0
  50. data/lib/tasks/install.rake +20 -0
  51. data/lib/templates/installer.rb +4 -0
  52. data/lib/templates/mailboxes/application_mailbox.rb +3 -0
  53. data/test/controllers/ingresses/amazon/inbound_emails_controller_test.rb +20 -0
  54. data/test/controllers/ingresses/mailgun/inbound_emails_controller_test.rb +89 -0
  55. data/test/controllers/ingresses/mandrill/inbound_emails_controller_test.rb +58 -0
  56. data/test/controllers/ingresses/postfix/inbound_emails_controller_test.rb +54 -0
  57. data/test/controllers/ingresses/sendgrid/inbound_emails_controller_test.rb +44 -0
  58. data/test/dummy/.babelrc +18 -0
  59. data/test/dummy/.gitignore +3 -0
  60. data/test/dummy/.postcssrc.yml +3 -0
  61. data/test/dummy/Rakefile +6 -0
  62. data/test/dummy/app/assets/config/manifest.js +3 -0
  63. data/test/dummy/app/assets/images/.keep +0 -0
  64. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  65. data/test/dummy/app/assets/stylesheets/scaffold.css +80 -0
  66. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  67. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  68. data/test/dummy/app/controllers/application_controller.rb +2 -0
  69. data/test/dummy/app/controllers/concerns/.keep +0 -0
  70. data/test/dummy/app/helpers/application_helper.rb +2 -0
  71. data/test/dummy/app/javascript/packs/application.js +0 -0
  72. data/test/dummy/app/jobs/application_job.rb +2 -0
  73. data/test/dummy/app/mailboxes/application_mailbox.rb +2 -0
  74. data/test/dummy/app/mailboxes/messages_mailbox.rb +4 -0
  75. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  76. data/test/dummy/app/models/application_record.rb +3 -0
  77. data/test/dummy/app/models/concerns/.keep +0 -0
  78. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  79. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  80. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  81. data/test/dummy/bin/bundle +3 -0
  82. data/test/dummy/bin/rails +4 -0
  83. data/test/dummy/bin/rake +4 -0
  84. data/test/dummy/bin/setup +36 -0
  85. data/test/dummy/bin/update +31 -0
  86. data/test/dummy/bin/yarn +11 -0
  87. data/test/dummy/config.ru +5 -0
  88. data/test/dummy/config/application.rb +19 -0
  89. data/test/dummy/config/boot.rb +5 -0
  90. data/test/dummy/config/cable.yml +10 -0
  91. data/test/dummy/config/database.yml +25 -0
  92. data/test/dummy/config/environment.rb +5 -0
  93. data/test/dummy/config/environments/development.rb +63 -0
  94. data/test/dummy/config/environments/production.rb +96 -0
  95. data/test/dummy/config/environments/test.rb +46 -0
  96. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  97. data/test/dummy/config/initializers/assets.rb +14 -0
  98. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  99. data/test/dummy/config/initializers/content_security_policy.rb +22 -0
  100. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  101. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  102. data/test/dummy/config/initializers/inflections.rb +16 -0
  103. data/test/dummy/config/initializers/mime_types.rb +4 -0
  104. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  105. data/test/dummy/config/locales/en.yml +33 -0
  106. data/test/dummy/config/puma.rb +34 -0
  107. data/test/dummy/config/routes.rb +4 -0
  108. data/test/dummy/config/spring.rb +6 -0
  109. data/test/dummy/config/storage.yml +35 -0
  110. data/test/dummy/config/webpack/development.js +3 -0
  111. data/test/dummy/config/webpack/environment.js +3 -0
  112. data/test/dummy/config/webpack/production.js +3 -0
  113. data/test/dummy/config/webpack/test.js +3 -0
  114. data/test/dummy/config/webpacker.yml +65 -0
  115. data/test/dummy/db/migrate/20180208205311_create_action_mailroom_tables.rb +11 -0
  116. data/test/dummy/db/migrate/20180212164506_create_active_storage_tables.active_storage.rb +26 -0
  117. data/test/dummy/db/schema.rb +43 -0
  118. data/test/dummy/lib/assets/.keep +0 -0
  119. data/test/dummy/log/.keep +0 -0
  120. data/test/dummy/package.json +11 -0
  121. data/test/dummy/public/404.html +67 -0
  122. data/test/dummy/public/422.html +67 -0
  123. data/test/dummy/public/500.html +66 -0
  124. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  125. data/test/dummy/public/apple-touch-icon.png +0 -0
  126. data/test/dummy/public/favicon.ico +0 -0
  127. data/test/dummy/storage/.keep +0 -0
  128. data/test/dummy/yarn.lock +6071 -0
  129. data/test/fixtures/files/welcome.eml +631 -0
  130. data/test/jobs/incineration_job_test.rb +17 -0
  131. data/test/test_helper.rb +54 -0
  132. data/test/unit/inbound_email/incineration_test.rb +45 -0
  133. data/test/unit/inbound_email/message_id_test.rb +13 -0
  134. data/test/unit/inbound_email_test.rb +13 -0
  135. data/test/unit/mail_ext/address_equality_test.rb +9 -0
  136. data/test/unit/mail_ext/address_wrapping_test.rb +11 -0
  137. data/test/unit/mail_ext/recipients_test.rb +33 -0
  138. data/test/unit/mailbox/bouncing_test.rb +29 -0
  139. data/test/unit/mailbox/callbacks_test.rb +75 -0
  140. data/test/unit/mailbox/routing_test.rb +30 -0
  141. data/test/unit/mailbox/state_test.rb +49 -0
  142. data/test/unit/postfix_relayer_test.rb +90 -0
  143. data/test/unit/router_test.rb +137 -0
  144. metadata +355 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 56440a6cdfeaf77350754266fba5e3e00ab5abf8e05518b22bccd76f69c80f1a
4
+ data.tar.gz: 1a3e0968725439b1b38f96f5e50c946e5b593f444946e40ed5dea94176d0ef14
5
+ SHA512:
6
+ metadata.gz: d28565e5cc3851cc5a33fb93d3989f93351b2346e64c737d576b1b4d3cef41b01f92aba573e394e9d66e34ff602c9d99ac302daafa50199f84abda8c9213cb22
7
+ data.tar.gz: 07ceb50601e8094c74c5217cf9546fb542682e77471161eeab7f73469826cfde43531d6a3e5731e26142eed509f2eea99381f671864337847be4ad14a9bbc1ac
@@ -0,0 +1,2 @@
1
+ .byebug_history
2
+ *.sqlite3-journal
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+ git_source(:github) { |repo_path| "https://github.com/#{repo_path}.git" }
3
+
4
+ gemspec
5
+
6
+ gem "rails", github: "rails/rails"
7
+
8
+ gem "aws-sdk-sns"
@@ -0,0 +1,159 @@
1
+ GIT
2
+ remote: https://github.com/rails/rails.git
3
+ revision: a1ee4a9ff9d4a3cb255365310ead0dc7b739c6be
4
+ specs:
5
+ actioncable (6.0.0.alpha)
6
+ actionpack (= 6.0.0.alpha)
7
+ nio4r (~> 2.0)
8
+ websocket-driver (>= 0.6.1)
9
+ actionmailer (6.0.0.alpha)
10
+ actionpack (= 6.0.0.alpha)
11
+ actionview (= 6.0.0.alpha)
12
+ activejob (= 6.0.0.alpha)
13
+ mail (~> 2.5, >= 2.5.4)
14
+ rails-dom-testing (~> 2.0)
15
+ actionpack (6.0.0.alpha)
16
+ actionview (= 6.0.0.alpha)
17
+ activesupport (= 6.0.0.alpha)
18
+ rack (~> 2.0)
19
+ rack-test (>= 0.6.3)
20
+ rails-dom-testing (~> 2.0)
21
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
22
+ actionview (6.0.0.alpha)
23
+ activesupport (= 6.0.0.alpha)
24
+ builder (~> 3.1)
25
+ erubi (~> 1.4)
26
+ rails-dom-testing (~> 2.0)
27
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
28
+ activejob (6.0.0.alpha)
29
+ activesupport (= 6.0.0.alpha)
30
+ globalid (>= 0.3.6)
31
+ activemodel (6.0.0.alpha)
32
+ activesupport (= 6.0.0.alpha)
33
+ activerecord (6.0.0.alpha)
34
+ activemodel (= 6.0.0.alpha)
35
+ activesupport (= 6.0.0.alpha)
36
+ activestorage (6.0.0.alpha)
37
+ actionpack (= 6.0.0.alpha)
38
+ activerecord (= 6.0.0.alpha)
39
+ marcel (~> 0.3.1)
40
+ activesupport (6.0.0.alpha)
41
+ concurrent-ruby (~> 1.0, >= 1.0.2)
42
+ i18n (>= 0.7, < 2)
43
+ minitest (~> 5.1)
44
+ tzinfo (~> 1.1)
45
+ rails (6.0.0.alpha)
46
+ actioncable (= 6.0.0.alpha)
47
+ actionmailer (= 6.0.0.alpha)
48
+ actionpack (= 6.0.0.alpha)
49
+ actionview (= 6.0.0.alpha)
50
+ activejob (= 6.0.0.alpha)
51
+ activemodel (= 6.0.0.alpha)
52
+ activerecord (= 6.0.0.alpha)
53
+ activestorage (= 6.0.0.alpha)
54
+ activesupport (= 6.0.0.alpha)
55
+ bundler (>= 1.3.0)
56
+ railties (= 6.0.0.alpha)
57
+ sprockets-rails (>= 2.0.0)
58
+ railties (6.0.0.alpha)
59
+ actionpack (= 6.0.0.alpha)
60
+ activesupport (= 6.0.0.alpha)
61
+ method_source
62
+ rake (>= 0.8.7)
63
+ thor (>= 0.19.0, < 2.0)
64
+
65
+ PATH
66
+ remote: .
67
+ specs:
68
+ actionmailbox (0.1.0)
69
+ rails (>= 5.2.0)
70
+
71
+ GEM
72
+ remote: https://rubygems.org/
73
+ specs:
74
+ addressable (2.5.2)
75
+ public_suffix (>= 2.0.2, < 4.0)
76
+ aws-eventstream (1.0.1)
77
+ aws-partitions (1.105.0)
78
+ aws-sdk-core (3.30.0)
79
+ aws-eventstream (~> 1.0)
80
+ aws-partitions (~> 1.0)
81
+ aws-sigv4 (~> 1.0)
82
+ jmespath (~> 1.0)
83
+ aws-sdk-sns (1.5.0)
84
+ aws-sdk-core (~> 3, >= 3.26.0)
85
+ aws-sigv4 (~> 1.0)
86
+ aws-sigv4 (1.0.3)
87
+ builder (3.2.3)
88
+ byebug (10.0.2)
89
+ concurrent-ruby (1.0.5)
90
+ crack (0.4.3)
91
+ safe_yaml (~> 1.0.0)
92
+ crass (1.0.4)
93
+ erubi (1.7.1)
94
+ globalid (0.4.1)
95
+ activesupport (>= 4.2.0)
96
+ hashdiff (0.3.7)
97
+ i18n (1.1.0)
98
+ concurrent-ruby (~> 1.0)
99
+ jmespath (1.4.0)
100
+ loofah (2.2.2)
101
+ crass (~> 1.0.2)
102
+ nokogiri (>= 1.5.9)
103
+ mail (2.7.1)
104
+ mini_mime (>= 0.1.1)
105
+ marcel (0.3.3)
106
+ mimemagic (~> 0.3.2)
107
+ method_source (0.9.0)
108
+ mimemagic (0.3.2)
109
+ mini_mime (1.0.1)
110
+ mini_portile2 (2.3.0)
111
+ minitest (5.11.3)
112
+ nio4r (2.3.1)
113
+ nokogiri (1.8.5)
114
+ mini_portile2 (~> 2.3.0)
115
+ public_suffix (3.0.3)
116
+ rack (2.0.5)
117
+ rack-test (1.1.0)
118
+ rack (>= 1.0, < 3)
119
+ rails-dom-testing (2.0.3)
120
+ activesupport (>= 4.2.0)
121
+ nokogiri (>= 1.6)
122
+ rails-html-sanitizer (1.0.4)
123
+ loofah (~> 2.2, >= 2.2.2)
124
+ rake (12.3.1)
125
+ safe_yaml (1.0.4)
126
+ sprockets (3.7.2)
127
+ concurrent-ruby (~> 1.0)
128
+ rack (> 1, < 3)
129
+ sprockets-rails (3.2.1)
130
+ actionpack (>= 4.0)
131
+ activesupport (>= 4.0)
132
+ sprockets (>= 3.0.0)
133
+ sqlite3 (1.3.13)
134
+ thor (0.20.0)
135
+ thread_safe (0.3.6)
136
+ tzinfo (1.2.5)
137
+ thread_safe (~> 0.1)
138
+ webmock (3.4.2)
139
+ addressable (>= 2.3.6)
140
+ crack (>= 0.3.2)
141
+ hashdiff
142
+ websocket-driver (0.7.0)
143
+ websocket-extensions (>= 0.1.0)
144
+ websocket-extensions (0.1.3)
145
+
146
+ PLATFORMS
147
+ ruby
148
+
149
+ DEPENDENCIES
150
+ actionmailbox!
151
+ aws-sdk-sns
152
+ bundler (~> 1.15)
153
+ byebug
154
+ rails!
155
+ sqlite3
156
+ webmock
157
+
158
+ BUNDLED WITH
159
+ 1.16.6
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Basecamp, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,278 @@
1
+ # Action Mailbox
2
+
3
+ Action Mailbox routes incoming emails to controller-like mailboxes for processing in Rails. It ships with ingresses for Amazon SES, Mailgun, Mandrill, and SendGrid. You can also handle inbound mails directly via the built-in Postfix ingress.
4
+
5
+ The inbound emails are turned into `InboundEmail` records using Active Record and feature lifecycle tracking, storage of the original email on cloud storage via Active Storage, and responsible data handling with on-by-default incineration.
6
+
7
+ These inbound emails are routed asynchronously using Active Job to one or several dedicated mailboxes, which are capable of interacting directly with the rest of your domain model.
8
+
9
+
10
+ ## How does this compare to Action Mailer's inbound processing?
11
+
12
+ Rails has long had an anemic way of [receiving emails using Action Mailer](https://guides.rubyonrails.org/action_mailer_basics.html#receiving-emails), but it was poorly flushed out, lacked cohesion with the task of sending emails, and offered no help on integrating with popular inbound email processing platforms. Action Mailbox supersedes the receiving part of Action Mailer, which will be deprecated in due course.
13
+
14
+
15
+ ## Installing
16
+
17
+ Assumes a Rails 5.2+ application:
18
+
19
+ 1. Install the gem:
20
+
21
+ ```ruby
22
+ # Gemfile
23
+ gem "actionmailbox", github: "rails/actionmailbox", require: "action_mailbox"
24
+ ```
25
+
26
+ 1. Install migrations needed for InboundEmail (and ensure Active Storage is setup)
27
+
28
+ ```
29
+ ./bin/rails action_mailbox:install
30
+ ./bin/rails db:migrate
31
+ ```
32
+
33
+ ## Configuring
34
+
35
+ ### Amazon SES
36
+
37
+ 1. Install the [`aws-sdk-sns`](https://rubygems.org/gems/aws-sdk-sns) gem:
38
+
39
+ ```ruby
40
+ # Gemfile
41
+ gem "aws-sdk-sns", ">= 1.9.0", require: false
42
+ ```
43
+
44
+ 2. Tell Action Mailbox to accept emails from SES:
45
+
46
+ ```ruby
47
+ # config/environments/production.rb
48
+ config.action_mailbox.ingress = :amazon
49
+ ```
50
+
51
+ 3. [Configure SES][ses-docs] to deliver emails to your application via POST requests
52
+ to `/rails/action_mailbox/amazon/inbound_emails`. If your application lived at `https://example.com`, you would specify
53
+ the fully-qualified URL `https://example.com/rails/action_mailbox/amazon/inbound_emails`.
54
+
55
+ [ses-docs]: https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-notifications.html
56
+
57
+ ### Mailgun
58
+
59
+ 1. Give Action Mailbox your [Mailgun API key][mailgun-api-key] so it can authenticate requests to the Mailgun ingress.
60
+
61
+ Use `rails credentials:edit` to add your API key to your application's encrypted credentials under
62
+ `action_mailbox.mailgun_api_key`, where Action Mailbox will automatically find it:
63
+
64
+ ```yaml
65
+ action_mailbox:
66
+ mailgun_api_key: ...
67
+ ```
68
+
69
+ Alternatively, provide your API key in the `MAILGUN_INGRESS_API_KEY` environment variable.
70
+
71
+ 2. Tell Action Mailbox to accept emails from Mailgun:
72
+
73
+ ```ruby
74
+ # config/environments/production.rb
75
+ config.action_mailbox.ingress = :mailgun
76
+ ```
77
+
78
+ 3. [Configure Mailgun][mailgun-forwarding] to forward inbound emails to `/rails/action_mailbox/mailgun/inbound_emails/mime`.
79
+ If your application lived at `https://example.com`, you would specify the fully-qualified URL
80
+ `https://example.com/rails/action_mailbox/mailgun/inbound_emails/mime`.
81
+
82
+ [mailgun-api-key]: https://help.mailgun.com/hc/en-us/articles/203380100-Where-can-I-find-my-API-key-and-SMTP-credentials-
83
+ [mailgun-forwarding]: https://documentation.mailgun.com/en/latest/user_manual.html#receiving-forwarding-and-storing-messages
84
+
85
+ ### Mandrill
86
+
87
+ 1. Give Action Mailbox your Mandrill API key so it can authenticate requests to the Mandrill ingress.
88
+
89
+ Use `rails credentials:edit` to add your API key to your application's encrypted credentials under
90
+ `action_mailbox.mandrill_api_key`, where Action Mailbox will automatically find it:
91
+
92
+ ```yaml
93
+ action_mailbox:
94
+ mandrill_api_key: ...
95
+ ```
96
+
97
+ Alternatively, provide your API key in the `MANDRILL_INGRESS_API_KEY` environment variable.
98
+
99
+ 2. Tell Action Mailbox to accept emails from Mandrill:
100
+
101
+ ```ruby
102
+ # config/environments/production.rb
103
+ config.action_mailbox.ingress = :mandrill
104
+ ```
105
+
106
+ 3. [Configure Mandrill][mandrill-routing] to route inbound emails to `/rails/action_mailbox/mandrill/inbound_emails`.
107
+ If your application lived at `https://example.com`, you would specify the fully-qualified URL
108
+ `https://example.com/rails/action_mailbox/mandrill/inbound_emails`.
109
+
110
+ [mandrill-routing]: https://mandrill.zendesk.com/hc/en-us/articles/205583197-Inbound-Email-Processing-Overview
111
+
112
+ ### Postfix
113
+
114
+ 1. Tell Action Mailbox to accept emails from Postfix:
115
+
116
+ ```ruby
117
+ # config/environments/production.rb
118
+ config.action_mailbox.ingress = :postfix
119
+ ```
120
+
121
+ 2. Generate a strong password that Action Mailbox can use to authenticate requests to the Postfix ingress.
122
+
123
+ Use `rails credentials:edit` to add the password to your application's encrypted credentials under
124
+ `action_mailbox.ingress_password`, where Action Mailbox will automatically find it:
125
+
126
+ ```yaml
127
+ action_mailbox:
128
+ ingress_password: ...
129
+ ```
130
+
131
+ Alternatively, provide the password in the `RAILS_INBOUND_EMAIL_PASSWORD` environment variable.
132
+
133
+ 3. [Configure Postfix][postfix-config] to pipe inbound emails to `bin/rails action_mailbox:ingress:postfix`, providing
134
+ the `URL` of the Postfix ingress and the `INGRESS_PASSWORD` you previously generated. If your application lived at
135
+ `https://example.com`, the full command would look like this:
136
+
137
+ ```
138
+ URL=https://example.com/rails/action_mailbox/postfix/inbound_emails INGRESS_PASSWORD=... bin/rails action_mailbox:ingress:postfix
139
+ ```
140
+
141
+ [postfix-config]: https://serverfault.com/questions/258469/how-to-configure-postfix-to-pipe-all-incoming-email-to-a-script
142
+
143
+ ### SendGrid
144
+
145
+ 1. Tell Action Mailbox to accept emails from SendGrid:
146
+
147
+ ```ruby
148
+ # config/environments/production.rb
149
+ config.action_mailbox.ingress = :sendgrid
150
+ ```
151
+
152
+ 2. Generate a strong password that Action Mailbox can use to authenticate requests to the SendGrid ingress.
153
+
154
+ Use `rails credentials:edit` to add the password to your application's encrypted credentials under
155
+ `action_mailbox.ingress_password`, where Action Mailbox will automatically find it:
156
+
157
+ ```yaml
158
+ action_mailbox:
159
+ ingress_password: ...
160
+ ```
161
+
162
+ Alternatively, provide the password in the `RAILS_INBOUND_EMAIL_PASSWORD` environment variable.
163
+
164
+ 3. [Configure SendGrid Inbound Parse][sendgrid-config] to forward inbound emails to
165
+ `/rails/action_mailbox/sendgrid/inbound_emails` with the username `actionmailbox` and the password you previously
166
+ generated. If your application lived at `https://example.com`, you would configure SendGrid with the following URL:
167
+
168
+ ```
169
+ https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/sendgrid/inbound_emails
170
+ ```
171
+
172
+ **⚠️ Note:** When configuring your SendGrid Inbound Parse webhook, be sure to check the box labeled **“Post the raw,
173
+ full MIME message.”** Action Mailbox needs the raw MIME message to work.
174
+
175
+ [sendgrid-config]: https://sendgrid.com/docs/for-developers/parsing-email/setting-up-the-inbound-parse-webhook/
176
+
177
+
178
+ ## Examples
179
+
180
+ Configure basic routing:
181
+
182
+ ```ruby
183
+ # app/models/message.rb
184
+ class ApplicationMailbox < ActionMailbox::Base
185
+ routing /^save@/i => :forwards
186
+ routing /@replies\./i => :replies
187
+ end
188
+ ```
189
+
190
+ Then setup a mailbox:
191
+
192
+ ```ruby
193
+ # app/mailboxes/forwards_mailbox.rb
194
+ class ForwardsMailbox < ApplicationMailbox
195
+ # Callbacks specify prerequisites to processing
196
+ before_processing :require_forward
197
+
198
+ def process
199
+ if forwarder.buckets.one?
200
+ record_forward
201
+ else
202
+ stage_forward_and_request_more_details
203
+ end
204
+ end
205
+
206
+ private
207
+ def require_forward
208
+ unless message.forward?
209
+ # Use Action Mailers to bounce incoming emails back to sender – this halts processing
210
+ bounce_with Forwards::BounceMailer.missing_forward(
211
+ inbound_email, forwarder: forwarder
212
+ )
213
+ end
214
+ end
215
+
216
+ def forwarder
217
+ @forwarder ||= Person.where(email_address: mail.from)
218
+ end
219
+
220
+ def record_forward
221
+ forwarder.buckets.first.record \
222
+ Forward.new forwarder: forwarder, subject: message.subject, content: mail.content
223
+ end
224
+
225
+ def stage_forward_and_request_more_details
226
+ Forwards::RoutingMailer.choose_project(mail).deliver_now
227
+ end
228
+ end
229
+ ```
230
+
231
+ ## Incineration of InboundEmails
232
+
233
+ By default, an InboundEmail that has been successfully processed will be incinerated after 30 days. This ensures you're not holding on to people's data willy-nilly after they may have canceled their accounts or deleted their content. The intention is that after you've processed an email, you should have extracted all the data you needed and turned it into domain models and content on your side of the application. The InboundEmail simply stays in the system for the extra time to provide debugging and forensics options.
234
+
235
+ The actual incineration is done via the `IncinerationJob` that's scheduled to run after `config.action_mailbox.incinerate_after` time. This value is by default set to `30.days`, but you can change it in your production.rb configuration. (Note that this far-future incineration scheduling relies on your job queue being able to hold jobs for that long.)
236
+
237
+
238
+ ## Working with Action Mailbox in development
239
+
240
+ It's helpful to be able to test incoming emails in development without actually sending and receiving real emails. To accomplish this, there's a conductor controller mounted at `/rails/conductor/action_mailbox/inbound_emails`, which gives you an index of all the InboundEmails in the system, their state of processing, and a form to create a new InboundEmail as well.
241
+
242
+
243
+ ## Testing mailboxes
244
+
245
+ Example:
246
+
247
+ ```ruby
248
+ class ForwardsMailboxTest < ActionMailbox::TestCase
249
+ test "directly recording a client forward for a forwarder and forwardee corresponding to one project" do
250
+ assert_difference -> { people(:david).buckets.first.recordings.count } do
251
+ receive_inbound_email_from_mail \
252
+ to: 'save@example.com',
253
+ from: people(:david).email_address,
254
+ subject: "Fwd: Status update?",
255
+ body: <<~BODY
256
+ --- Begin forwarded message ---
257
+ From: Frank Holland <frank@microsoft.com>
258
+
259
+ What's the status?
260
+ BODY
261
+ end
262
+
263
+ recording = people(:david).buckets.first.recordings.last
264
+ assert_equal people(:david), recording.creator
265
+ assert_equal "Status update?", recording.forward.subject
266
+ assert_match "What's the status?", recording.forward.content.to_s
267
+ end
268
+ end
269
+ ```
270
+
271
+
272
+ ## Development road map
273
+
274
+ Action Mailbox is destined for inclusion in Rails 6, which is due to be released some time in 2019. We will refine the framework in this separate rails/actionmailbox repository until we're ready to promote it via a pull request to rails/rails.
275
+
276
+ ## License
277
+
278
+ Action Mailbox is released under the [MIT License](https://opensource.org/licenses/MIT).