mailkick 1.4.0 → 2.0.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: 4f16bb5b552006639122c94a4b910982ed0845694bb503dfac08e23e3ed41f24
4
- data.tar.gz: c82b3290c80af68f5d9817a38a4d30af8d5893a216121154182457d96f0904dc
3
+ metadata.gz: 6d3592c25635ef911ea75b5bd5f07bbf512cbc3066bda2a7a7deb39e0d969821
4
+ data.tar.gz: 521daa545720254493b7dbef3806a95e44ac739041e6a9ab4fd33a5ef41881b7
5
5
  SHA512:
6
- metadata.gz: a31f0bb8e3a84e0abcc6bca052a815fd4f98eb8130572903e010d479bc3e46c219ea0efb4b89cfba09d8cb3e7fd68a42f7c99716a1e4228ea9b65af6e663b21e
7
- data.tar.gz: e9e0cc2fac1d027ee28650077977d30abcce514e4064eb27b73e0650147828f292bcae32631cdb600271b9a5e50ddd24603e1d328231a1227d523571cfdb003c
6
+ metadata.gz: fb7e324a4812673f7278bb8ce890ea0d67ac74c6e923ae4d43dc0c6e447fa28282a56beeb25c86fe9d5e9e34d3ecda03099d2240773b5d74ca3a940cc4b8c6e5
7
+ data.tar.gz: ce6f5ba95efec816c0571189a3e2f1a99060fe389927656836110a750f61fe768810b7f2a342325081acb6d5b7e66f1464db1b449fbc5f1467a4e595d9a3c51c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 2.0.0 (2025-06-23)
2
+
3
+ See the [upgrade notes](https://github.com/ankane/mailkick?tab=readme-ov-file#20)
4
+
5
+ - Added `prefix` option to `has_subscriptions`
6
+ - Switched to suppressions API for Postmark and changed default stream to `broadcast`
7
+ - Dropped support for legacy opt-outs
8
+ - Dropped support for legacy secret token generation and serialization
9
+ - Dropped support for legacy SendGrid service
10
+
1
11
  ## 1.4.0 (2025-04-03)
2
12
 
3
13
  - Dropped support for Ruby < 3.2 and Rails < 7.1
data/README.md CHANGED
@@ -5,6 +5,8 @@ Email subscriptions for Rails
5
5
  - Add one-click unsubscribe links and headers to your emails
6
6
  - Fetch bounces and spam reports from your email service
7
7
 
8
+ **Mailkick 2.0 was recently released** - see [how to upgrade](#upgrading)
9
+
8
10
  :postbox: Check out [Ahoy Email](https://github.com/ankane/ahoy_email) for analytics
9
11
 
10
12
  [![Build Status](https://github.com/ankane/mailkick/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/mailkick/actions)
@@ -126,8 +128,6 @@ The following services are supported:
126
128
  - [Postmark](#postmark)
127
129
  - [SendGrid](#sendgrid)
128
130
 
129
- Will gladly accept pull requests for others.
130
-
131
131
  #### AWS SES
132
132
 
133
133
  Add the gem
@@ -168,7 +168,7 @@ Add the gem
168
168
  gem "mandrill-api"
169
169
  ```
170
170
 
171
- And set `ENV["MANDRILL_APIKEY"]`.
171
+ And set `ENV["MANDRILL_API_KEY"]`.
172
172
 
173
173
  #### Postmark
174
174
 
@@ -209,6 +209,30 @@ Access the subscription model directly
209
209
  Mailkick::Subscription.all
210
210
  ```
211
211
 
212
+ Prefix method names with `mailkick_`
213
+
214
+ ```ruby
215
+ class User < ApplicationRecord
216
+ has_subscriptions prefix: true
217
+ end
218
+ ```
219
+
220
+ ## Upgrading
221
+
222
+ ### 2.0
223
+
224
+ Unsubscribe links created before version 1.1.1 will no longer work by default. Determine if this is acceptable for your application (for instance, in the US, links must work [for 30 days](https://www.ftc.gov/business-guidance/resources/can-spam-act-compliance-guide-business) after the message is sent). To restore support, [determine the previous secret token](https://github.com/ankane/mailkick/blob/v1.4.0/lib/mailkick/engine.rb#L13-L22) and create an initializer with:
225
+
226
+ ```ruby
227
+ Mailkick.message_verifier.rotate(previous_secret_token, serializer: Marshal)
228
+ ```
229
+
230
+ If fetching bounces, spam reports, and unsubscribes from Postmark, the suppressions API is now used and the default stream has been changed to `broadcast`. To use `outbound`, create an initializer with:
231
+
232
+ ```ruby
233
+ Mailkick.services = [Mailkick::Service::Postmark.new(api_key: api_key, stream_id: "outbound")]
234
+ ```
235
+
212
236
  ## History
213
237
 
214
238
  View the [changelog](https://github.com/ankane/mailkick/blob/master/CHANGELOG.md)
@@ -11,8 +11,6 @@ module Mailkick
11
11
  def unsubscribe
12
12
  subscription.delete_all
13
13
 
14
- Mailkick::Legacy.opt_out(legacy_options) if Mailkick::Legacy.opt_outs?
15
-
16
14
  if request.post? && params["List-Unsubscribe"] == "One-Click"
17
15
  # must not redirect according to RFC 8058
18
16
  # could render show action instead
@@ -25,8 +23,6 @@ module Mailkick
25
23
  def subscribe
26
24
  subscription.first_or_create!
27
25
 
28
- Mailkick::Legacy.opt_in(legacy_options) if Mailkick::Legacy.opt_outs?
29
-
30
26
  redirect_to subscription_path(params[:id])
31
27
  end
32
28
 
@@ -65,17 +61,5 @@ module Mailkick
65
61
  unsubscribe_subscription_path(params[:id])
66
62
  end
67
63
  helper_method :unsubscribe_url
68
-
69
- def legacy_options
70
- if @subscriber_type
71
- # on the unprobabilistic chance subscriber_type is compromised, not much damage
72
- user = @subscriber_type.constantize.find(@subscriber_id)
73
- end
74
- {
75
- email: @email,
76
- user: user,
77
- list: @list
78
- }
79
- end
80
64
  end
81
65
  end
@@ -14,6 +14,18 @@ module Mailkick
14
14
  def migration_version
15
15
  "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
16
16
  end
17
+
18
+ def primary_key_type
19
+ ", id: :#{key_type}" if key_type
20
+ end
21
+
22
+ def foreign_key_type
23
+ ", type: :#{key_type}" if key_type
24
+ end
25
+
26
+ def key_type
27
+ Rails.configuration.generators.options.dig(:active_record, :primary_key_type)
28
+ end
17
29
  end
18
30
  end
19
31
  end
@@ -1,7 +1,7 @@
1
1
  class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
- create_table :mailkick_subscriptions do |t|
4
- t.references :subscriber, polymorphic: true, index: false
3
+ create_table :mailkick_subscriptions<%= primary_key_type %> do |t|
4
+ t.references :subscriber<%= foreign_key_type %>, polymorphic: true, index: false
5
5
  t.string :list
6
6
  t.timestamps
7
7
  end
@@ -3,7 +3,7 @@ require "rails/generators"
3
3
  module Mailkick
4
4
  module Generators
5
5
  class ViewsGenerator < Rails::Generators::Base
6
- source_root File.expand_path("../../../../app/views", __FILE__)
6
+ source_root File.expand_path("../../../app/views", __dir__)
7
7
 
8
8
  def copy_initializer_file
9
9
  directory "mailkick", "app/views/mailkick"
@@ -5,23 +5,7 @@ module Mailkick
5
5
  initializer "mailkick" do |app|
6
6
  Mailkick.discover_services unless Mailkick.services.any?
7
7
 
8
- unless Mailkick.secret_token
9
- Mailkick.secret_token = app.key_generator.generate_key("mailkick")
10
- Mailkick.message_verifier = ActiveSupport::MessageVerifier.new(Mailkick.secret_token, serializer: JSON)
11
-
12
- # TODO remove in 2.0
13
- creds =
14
- if app.respond_to?(:credentials) && app.credentials.secret_key_base
15
- app.credentials
16
- elsif app.respond_to?(:secrets) && app.config.paths["config/secrets"].existent.any?
17
- app.secrets
18
- else
19
- app.config
20
- end
21
-
22
- token = creds.respond_to?(:secret_key_base) ? creds.secret_key_base : creds.secret_token
23
- Mailkick.message_verifier.rotate(token, serializer: Marshal) if token
24
- end
8
+ Mailkick.secret_token ||= app.key_generator.generate_key("mailkick")
25
9
  end
26
10
  end
27
11
  end
@@ -1,21 +1,23 @@
1
1
  module Mailkick
2
2
  module Model
3
- def has_subscriptions
3
+ def has_subscriptions(prefix: false)
4
+ prefix = prefix ? "mailkick_" : ""
5
+
4
6
  class_eval do
5
7
  has_many :mailkick_subscriptions, class_name: "Mailkick::Subscription", as: :subscriber
6
- scope :subscribed, ->(list) { joins(:mailkick_subscriptions).where(mailkick_subscriptions: {list: list}) }
8
+ scope "#{prefix}subscribed", ->(list) { joins(:mailkick_subscriptions).where(mailkick_subscriptions: {list: list}) }
7
9
 
8
- def subscribe(list)
10
+ define_method("#{prefix}subscribe") do |list|
9
11
  mailkick_subscriptions.where(list: list).first_or_create!
10
12
  nil
11
13
  end
12
14
 
13
- def unsubscribe(list)
15
+ define_method("#{prefix}unsubscribe") do |list|
14
16
  mailkick_subscriptions.where(list: list).delete_all
15
17
  nil
16
18
  end
17
19
 
18
- def subscribed?(list)
20
+ define_method("#{prefix}subscribed?") do |list|
19
21
  mailkick_subscriptions.where(list: list).exists?
20
22
  end
21
23
  end
@@ -8,8 +8,7 @@ module Mailkick
8
8
  "COMPLAINT" => "spam"
9
9
  }
10
10
 
11
- def initialize(options = {})
12
- @options = options
11
+ def initialize
13
12
  end
14
13
 
15
14
  def opt_outs
@@ -3,9 +3,9 @@
3
3
  module Mailkick
4
4
  class Service
5
5
  class Mailchimp < Mailkick::Service
6
- def initialize(options = {})
7
- @gibbon = ::Gibbon::Request.new(api_key: options[:api_key] || ENV["MAILCHIMP_API_KEY"])
8
- @list_id = options[:list_id] || ENV["MAILCHIMP_LIST_ID"]
6
+ def initialize(api_key: nil, list_id: nil)
7
+ @gibbon = ::Gibbon::Request.new(api_key: api_key || ENV["MAILCHIMP_API_KEY"])
8
+ @list_id = list_id || ENV["MAILCHIMP_LIST_ID"]
9
9
  end
10
10
 
11
11
  # TODO paginate
@@ -3,10 +3,10 @@
3
3
  module Mailkick
4
4
  class Service
5
5
  class Mailgun < Mailkick::Service
6
- def initialize(options = {})
6
+ def initialize(api_key: nil, domain: nil)
7
7
  require "mailgun"
8
- mailgun_client = ::Mailgun::Client.new(options[:api_key] || ENV["MAILGUN_API_KEY"])
9
- domain = options[:domain] || ActionMailer::Base.smtp_settings[:domain]
8
+ mailgun_client = ::Mailgun::Client.new(api_key || ENV["MAILGUN_API_KEY"])
9
+ domain ||= ActionMailer::Base.smtp_settings[:domain]
10
10
  @mailgun_events = ::Mailgun::Events.new(mailgun_client, domain)
11
11
  end
12
12
 
@@ -10,11 +10,10 @@ module Mailkick
10
10
  "unsub" => "unsubscribe"
11
11
  }
12
12
 
13
- # TODO remove ENV["MANDRILL_APIKEY"]
14
- def initialize(options = {})
13
+ def initialize(api_key: nil)
15
14
  require "mandrill"
16
15
  @mandrill = ::Mandrill::API.new(
17
- options[:api_key] || ENV["MANDRILL_APIKEY"] || ENV["MANDRILL_API_KEY"]
16
+ api_key || ENV["MANDRILL_APIKEY"] || ENV["MANDRILL_API_KEY"]
18
17
  )
19
18
  end
20
19
 
@@ -29,7 +28,6 @@ module Mailkick
29
28
  end
30
29
  end
31
30
 
32
- # TODO remove ENV["MANDRILL_APIKEY"]
33
31
  def self.discoverable?
34
32
  !!(defined?(::Mandrill::API) && (ENV["MANDRILL_APIKEY"] || ENV["MANDRILL_API_KEY"]))
35
33
  end
@@ -1,24 +1,25 @@
1
- # https://github.com/wildbit/postmark-gem
1
+ # https://github.com/ActiveCampaign/postmark-gem
2
2
 
3
3
  module Mailkick
4
4
  class Service
5
5
  class Postmark < Mailkick::Service
6
6
  REASONS_MAP = {
7
- "SpamNotification" => "spam",
7
+ "HardBounce" => "bounce",
8
8
  "SpamComplaint" => "spam",
9
- "Unsubscribe" => "unsubscribe"
9
+ "ManualSuppression" => "unsubscribe"
10
10
  }
11
11
 
12
- def initialize(options = {})
13
- @client = ::Postmark::ApiClient.new(options[:api_key] || ENV["POSTMARK_API_KEY"])
12
+ def initialize(api_key: nil, stream_id: "broadcast")
13
+ @client = ::Postmark::ApiClient.new(api_key || ENV["POSTMARK_API_KEY"])
14
+ @stream_id = stream_id
14
15
  end
15
16
 
16
17
  def opt_outs
17
- bounces
18
+ suppressions
18
19
  end
19
20
 
20
- def bounces
21
- fetch(@client.bounces)
21
+ def suppressions
22
+ fetch(@client.dump_suppressions(@stream_id))
22
23
  end
23
24
 
24
25
  def self.discoverable?
@@ -30,9 +31,9 @@ module Mailkick
30
31
  def fetch(response)
31
32
  response.map do |record|
32
33
  {
33
- email: record[:email],
34
- time: ActiveSupport::TimeZone["UTC"].parse(record[:bounced_at]),
35
- reason: REASONS_MAP.fetch(record[:type], "bounce")
34
+ email: record[:email_address],
35
+ time: ActiveSupport::TimeZone["UTC"].parse(record[:created_at]),
36
+ reason: REASONS_MAP[record[:suppression_reason]]
36
37
  }
37
38
  end
38
39
  end
@@ -1,41 +1,48 @@
1
- # https://github.com/freerobby/sendgrid_toolkit
1
+ # https://github.com/sendgrid/sendgrid-ruby
2
2
 
3
3
  module Mailkick
4
4
  class Service
5
5
  class SendGrid < Mailkick::Service
6
- def initialize(options = {})
7
- @api_user = options[:api_user] || ENV["SENDGRID_USERNAME"]
8
- @api_key = options[:api_key] || ENV["SENDGRID_PASSWORD"]
6
+ def initialize(api_key: nil)
7
+ @api_key = api_key || ENV["SENDGRID_API_KEY"]
9
8
  end
10
9
 
11
- # TODO paginate
12
10
  def opt_outs
13
11
  unsubscribes + spam_reports + bounces
14
12
  end
15
13
 
16
14
  def unsubscribes
17
- fetch(::SendgridToolkit::Unsubscribes, "unsubscribe")
15
+ fetch(client.suppression.unsubscribes, "unsubscribe")
18
16
  end
19
17
 
20
18
  def spam_reports
21
- fetch(::SendgridToolkit::SpamReports, "spam")
19
+ fetch(client.suppression.spam_reports, "spam")
22
20
  end
23
21
 
24
22
  def bounces
25
- fetch(::SendgridToolkit::Bounces, "bounce")
23
+ fetch(client.suppression.bounces, "bounce")
26
24
  end
27
25
 
28
26
  def self.discoverable?
29
- !!(defined?(::SendgridToolkit) && ENV["SENDGRID_USERNAME"] && ENV["SENDGRID_PASSWORD"])
27
+ !!(defined?(::SendGrid::API) && ENV["SENDGRID_API_KEY"])
30
28
  end
31
29
 
32
30
  protected
33
31
 
34
- def fetch(klass, reason)
35
- klass.new(@api_user, @api_key).retrieve_with_timestamps.map do |record|
32
+ def client
33
+ @client ||= ::SendGrid::API.new(api_key: @api_key).client
34
+ end
35
+
36
+ def fetch(query, reason)
37
+ # TODO paginate
38
+ response = query.get
39
+
40
+ raise "Bad status code: #{response.status_code}" if response.status_code.to_i != 200
41
+
42
+ response.parsed_body.map do |record|
36
43
  {
37
- email: record["email"],
38
- time: record["created"],
44
+ email: record[:email],
45
+ time: Time.at(record[:created]),
39
46
  reason: reason
40
47
  }
41
48
  end
@@ -43,6 +50,6 @@ module Mailkick
43
50
  end
44
51
 
45
52
  # backwards compatibility
46
- Sendgrid = SendGrid
53
+ SendGridV2 = SendGrid
47
54
  end
48
55
  end
@@ -1,3 +1,3 @@
1
1
  module Mailkick
2
- VERSION = "1.4.0"
2
+ VERSION = "2.0.0"
3
3
  end
data/lib/mailkick.rb CHANGED
@@ -6,16 +6,13 @@ require "json"
6
6
  require "set"
7
7
 
8
8
  # modules
9
- require_relative "mailkick/legacy"
10
9
  require_relative "mailkick/model"
11
- require_relative "mailkick/serializer"
12
10
  require_relative "mailkick/service"
13
11
  require_relative "mailkick/service/aws_ses"
14
12
  require_relative "mailkick/service/mailchimp"
15
13
  require_relative "mailkick/service/mailgun"
16
14
  require_relative "mailkick/service/mandrill"
17
15
  require_relative "mailkick/service/sendgrid"
18
- require_relative "mailkick/service/sendgrid_v2"
19
16
  require_relative "mailkick/service/postmark"
20
17
  require_relative "mailkick/url_helper"
21
18
  require_relative "mailkick/version"
@@ -34,12 +31,14 @@ module Mailkick
34
31
 
35
32
  def self.fetch_opt_outs
36
33
  services.each(&:fetch_opt_outs)
34
+ nil
37
35
  end
38
36
 
39
37
  def self.discover_services
40
38
  Service.subclasses.each do |service|
41
39
  services << service.new if service.discoverable?
42
40
  end
41
+ nil
43
42
  end
44
43
 
45
44
  def self.secret_token=(token)
@@ -48,7 +47,7 @@ module Mailkick
48
47
  end
49
48
 
50
49
  def self.message_verifier
51
- @@message_verifier ||= ActiveSupport::MessageVerifier.new(Mailkick.secret_token, serializer: Serializer)
50
+ @@message_verifier ||= ActiveSupport::MessageVerifier.new(Mailkick.secret_token, serializer: JSON)
52
51
  end
53
52
 
54
53
  def self.generate_token(subscriber, list)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailkick
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-03 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -32,7 +32,6 @@ files:
32
32
  - LICENSE.txt
33
33
  - README.md
34
34
  - app/controllers/mailkick/subscriptions_controller.rb
35
- - app/models/mailkick/opt_out.rb
36
35
  - app/models/mailkick/subscription.rb
37
36
  - app/views/mailkick/subscriptions/show.html.erb
38
37
  - config/routes.rb
@@ -41,9 +40,7 @@ files:
41
40
  - lib/generators/mailkick/views_generator.rb
42
41
  - lib/mailkick.rb
43
42
  - lib/mailkick/engine.rb
44
- - lib/mailkick/legacy.rb
45
43
  - lib/mailkick/model.rb
46
- - lib/mailkick/serializer.rb
47
44
  - lib/mailkick/service.rb
48
45
  - lib/mailkick/service/aws_ses.rb
49
46
  - lib/mailkick/service/mailchimp.rb
@@ -51,7 +48,6 @@ files:
51
48
  - lib/mailkick/service/mandrill.rb
52
49
  - lib/mailkick/service/postmark.rb
53
50
  - lib/mailkick/service/sendgrid.rb
54
- - lib/mailkick/service/sendgrid_v2.rb
55
51
  - lib/mailkick/url_helper.rb
56
52
  - lib/mailkick/version.rb
57
53
  homepage: https://github.com/ankane/mailkick
@@ -72,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
68
  - !ruby/object:Gem::Version
73
69
  version: '0'
74
70
  requirements: []
75
- rubygems_version: 3.6.2
71
+ rubygems_version: 3.6.7
76
72
  specification_version: 4
77
73
  summary: Email subscriptions for Rails
78
74
  test_files: []
@@ -1,7 +0,0 @@
1
- module Mailkick
2
- class OptOut < ActiveRecord::Base
3
- self.table_name = "mailkick_opt_outs"
4
-
5
- belongs_to :user, polymorphic: true, optional: true
6
- end
7
- end
@@ -1,70 +0,0 @@
1
- module Mailkick
2
- module Legacy
3
- # checks for table as long as it exists
4
- def self.opt_outs?
5
- unless defined?(@opt_outs) && @opt_outs == false
6
- @opt_outs = ActiveRecord::Base.connection_pool.with_connection { |c| c.table_exists?("mailkick_opt_outs") }
7
- end
8
- @opt_outs
9
- end
10
-
11
- def self.opted_out?(options)
12
- opt_outs(options).any?
13
- end
14
-
15
- def self.opt_out(options)
16
- unless opted_out?(options)
17
- time = options[:time] || Time.now
18
- Mailkick::OptOut.create! do |o|
19
- o.email = options[:email]
20
- o.user = options[:user]
21
- o.reason = options[:reason] || "unsubscribe"
22
- o.list = options[:list]
23
- o.created_at = time
24
- o.updated_at = time
25
- end
26
- end
27
- true
28
- end
29
-
30
- def self.opt_in(options)
31
- opt_outs(options).each do |opt_out|
32
- opt_out.active = false
33
- opt_out.save!
34
- end
35
- true
36
- end
37
-
38
- def self.opt_outs(options = {})
39
- relation = Mailkick::OptOut.where(active: true)
40
-
41
- contact_relation = Mailkick::OptOut.none
42
- if (email = options[:email])
43
- contact_relation = contact_relation.or(Mailkick::OptOut.where(email: email))
44
- end
45
- if (user = options[:user])
46
- contact_relation = contact_relation.or(
47
- Mailkick::OptOut.where("user_id = ? AND user_type = ?", user.id, user.class.name)
48
- )
49
- end
50
- relation = relation.merge(contact_relation) if email || user
51
-
52
- relation =
53
- if options[:list]
54
- relation.where("list IS NULL OR list = ?", options[:list])
55
- else
56
- relation.where("list IS NULL")
57
- end
58
-
59
- relation
60
- end
61
-
62
- def self.opted_out_emails(options = {})
63
- Set.new(opt_outs(options).where.not(email: nil).distinct.pluck(:email))
64
- end
65
-
66
- def self.opted_out_users(options = {})
67
- Set.new(opt_outs(options).where.not(user_id: nil).map(&:user))
68
- end
69
- end
70
- end
@@ -1,15 +0,0 @@
1
- # need custom class due to
2
- # https://github.com/rails/rails/issues/47185
3
- module Mailkick
4
- class Serializer
5
- def self.dump(value)
6
- ActiveSupport::JSON.encode(value)
7
- end
8
-
9
- def self.load(value)
10
- ActiveSupport::JSON.decode(value)
11
- rescue JSON::ParserError
12
- Marshal.load(value)
13
- end
14
- end
15
- end
@@ -1,52 +0,0 @@
1
- # https://github.com/sendgrid/sendgrid-ruby
2
-
3
- module Mailkick
4
- class Service
5
- class SendGridV2 < Mailkick::Service
6
- def initialize(options = {})
7
- @api_key = options[:api_key] || ENV["SENDGRID_API_KEY"]
8
- end
9
-
10
- def opt_outs
11
- unsubscribes + spam_reports + bounces
12
- end
13
-
14
- def unsubscribes
15
- fetch(client.suppression.unsubscribes, "unsubscribe")
16
- end
17
-
18
- def spam_reports
19
- fetch(client.suppression.spam_reports, "spam")
20
- end
21
-
22
- def bounces
23
- fetch(client.suppression.bounces, "bounce")
24
- end
25
-
26
- def self.discoverable?
27
- !!(defined?(::SendGrid::API) && ENV["SENDGRID_API_KEY"])
28
- end
29
-
30
- protected
31
-
32
- def client
33
- @client ||= ::SendGrid::API.new(api_key: @api_key).client
34
- end
35
-
36
- def fetch(query, reason)
37
- # TODO paginate
38
- response = query.get
39
-
40
- raise "Bad status code: #{response.status_code}" if response.status_code.to_i != 200
41
-
42
- response.parsed_body.map do |record|
43
- {
44
- email: record[:email],
45
- time: Time.at(record[:created]),
46
- reason: reason
47
- }
48
- end
49
- end
50
- end
51
- end
52
- end