noticed 1.5.5 → 1.5.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90b43c50384068092455c10cd6686fc46cd766121c0ea5cf593bbc4c8306e8de
4
- data.tar.gz: 23edb385b0c9981dd9c292c8f4c15ce6fadd9abb131adc1eb6440ab39c5fa186
3
+ metadata.gz: 85a89c655531764a6b6c97213a6b1368c21bee48e55298cb35be1154c32534ff
4
+ data.tar.gz: 404987e57e6f834fe21dc9b895664643767ed08e0d23e05c32cb31ebe0a8c5ab
5
5
  SHA512:
6
- metadata.gz: 9a5d92df6618d53f8be91ca71bdf6dfc6ff0b66279662a5690c6efaa162939f2ae4fe1868956427ff10a83fa77329c16c96af3faef9e2fa48e441f1478c91ed0
7
- data.tar.gz: 6d485a661fe14b24811479615e125cb4bf2794808dd28b39c102e677b1a1e99a0f22807acbc75a728442764f521c498ffa884998e243dacad5ea9e4e4cd2ca19
6
+ metadata.gz: 2cd89b9b0d0c0b76792861aba31db3402effab9136574425abfa44cb26130be894166834b1d80aca357c7d811c81d9f32dc5d5f4ad2e8bb68096310c9a8a7a1e
7
+ data.tar.gz: ec0b3c75c8976cd3e4789e54ca8d1744dad95d118f3c4d20a648153422425f03b8a34f1a5b16ed583db85eb3bfe778c996d354d168ac32765421b8a0bd33b8c6
data/README.md CHANGED
@@ -16,6 +16,7 @@ Currently, we support these notification delivery methods out of the box:
16
16
  * Twilio (SMS)
17
17
  * Vonage / Nexmo (SMS)
18
18
  * iOS Apple Push Notifications
19
+ * Firebase Cloud Messaging (Android and more)
19
20
 
20
21
  And you can easily add new notification types for any other delivery methods.
21
22
 
@@ -190,6 +191,31 @@ Example:
190
191
  deliver_by :slack, debug: true
191
192
  ```
192
193
 
194
+ ## ✅ Best Practices
195
+
196
+ A common use case is to trigger a notification when a record is created. For example,
197
+
198
+ ```ruby
199
+ class Message < ApplicationRecord
200
+ belongs_to :recipient, class_name: "User"
201
+
202
+ after_create_commit :notify_recipient
203
+
204
+ private
205
+
206
+ def notify_recipient
207
+ NewMessageNotification.with(message: self).deliver_later(recipient)
208
+ end
209
+ ```
210
+
211
+ If you are creating the notification on a background job (i.e. via `#deliver_later`), make sure you use a `commit` hook such as `after_create_commit` or `after_commit`.
212
+
213
+ Using `after_create` might cause the notification delivery methods to fail. This is because the job was enqueued while inside a database transaction, and the `Message` record might not yet be saved to the database.
214
+
215
+ A common symptom of this problem is undelivered notifications and the following error in your logs.
216
+
217
+ > `Discarded Noticed::DeliveryMethods::Email due to a ActiveJob::DeserializationError.`
218
+
193
219
  ## 🚛 Delivery Methods
194
220
 
195
221
  The delivery methods are designed to be modular so you can customize the way each type gets delivered.
@@ -205,6 +231,7 @@ For example, emails will require a subject, body, and email address while an SMS
205
231
  * [Test](docs/delivery_methods/test.md)
206
232
  * [Twilio](docs/delivery_methods/twilio.md)
207
233
  * [Vonage](docs/delivery_methods/vonage.md)
234
+ * [Firebase Cloud Messaging](docs/delivery_methods/fcm.md)
208
235
 
209
236
  ### Fallback Notifications
210
237
 
@@ -29,8 +29,8 @@ module Noticed
29
29
  end
30
30
  end
31
31
 
32
- def perform(args)
33
- @notification = args[:notification_class].constantize.new(args[:params])
32
+ def assign_args(args)
33
+ @notification = args.fetch(:notification_class).constantize.new(args[:params])
34
34
  @options = args[:options] || {}
35
35
  @params = args[:params]
36
36
  @recipient = args[:recipient]
@@ -39,6 +39,11 @@ module Noticed
39
39
  # Make notification aware of database record and recipient during delivery
40
40
  @notification.record = args[:record]
41
41
  @notification.recipient = args[:recipient]
42
+ self
43
+ end
44
+
45
+ def perform(args)
46
+ assign_args(args)
42
47
 
43
48
  return if (condition = @options[:if]) && !@notification.send(condition)
44
49
  return if (condition = @options[:unless]) && @notification.send(condition)
@@ -57,16 +62,16 @@ module Noticed
57
62
  # Helper method for making POST requests from delivery methods
58
63
  #
59
64
  # Usage:
60
- # post("http://example.com", basic_auth: {user:, pass:}, json: {}, form: {})
65
+ # post("http://example.com", basic_auth: {user:, pass:}, headers: {}, json: {}, form: {})
61
66
  #
62
67
  def post(url, args = {})
68
+ options ||= {}
63
69
  basic_auth = args.delete(:basic_auth)
70
+ headers = args.delete(:headers)
64
71
 
65
- request = if basic_auth
66
- HTTP.basic_auth(user: basic_auth[:user], pass: basic_auth[:pass])
67
- else
68
- HTTP
69
- end
72
+ request = HTTP
73
+ request = request.basic_auth(user: basic_auth[:user], pass: basic_auth[:pass]) if basic_auth
74
+ request = request.headers(headers) if headers
70
75
 
71
76
  response = request.post(url, args)
72
77
 
@@ -76,6 +81,8 @@ module Noticed
76
81
  end
77
82
 
78
83
  if !options[:ignore_failure] && !response.status.success?
84
+ puts response.status
85
+ puts response.body
79
86
  raise ResponseUnsuccessful.new(response)
80
87
  end
81
88
 
@@ -0,0 +1,92 @@
1
+ require "googleauth"
2
+
3
+ # class CommentNotifier
4
+ # deliver_by :fcm, credentials: Rails.root.join("config/certs/fcm.json"), format: :format_notification
5
+ #
6
+ # deliver_by :fcm, credentials: :fcm_credentials
7
+ # def fcm_credentials
8
+ # { project_id: "api-12345" }
9
+ # end
10
+ # end
11
+
12
+ module Noticed
13
+ module DeliveryMethods
14
+ class Fcm < Base
15
+ BASE_URI = "https://fcm.googleapis.com/v1/projects/"
16
+
17
+ option :format
18
+
19
+ def deliver
20
+ device_tokens.each do |device_token|
21
+ post("#{BASE_URI}#{project_id}/messages:send", headers: {authorization: "Bearer #{access_token}"}, json: {message: format(device_token)})
22
+ rescue ResponseUnsuccessful
23
+ cleanup_invalid_token(device_token)
24
+ end
25
+ end
26
+
27
+ def cleanup_invalid_token(device_token)
28
+ return unless notification.respond_to?(:cleanup_device_token)
29
+ notification.send(:cleanup_device_token, token: device_token, platform: "fcm")
30
+ end
31
+
32
+ def credentials
33
+ @credentials ||= begin
34
+ option = options[:credentials]
35
+ credentials_hash = case option
36
+ when Hash
37
+ option
38
+ when Pathname
39
+ load_json(option)
40
+ when String
41
+ load_json(Rails.root.join(option))
42
+ when Symbol
43
+ notification.send(option)
44
+ else
45
+ Rails.application.credentials.fcm
46
+ end
47
+
48
+ credentials_hash.symbolize_keys
49
+ end
50
+ end
51
+
52
+ def load_json(path)
53
+ JSON.parse(File.read(path))
54
+ end
55
+
56
+ def project_id
57
+ credentials[:project_id]
58
+ end
59
+
60
+ def access_token
61
+ token = authorizer.fetch_access_token!
62
+ token["access_token"]
63
+ end
64
+
65
+ def authorizer
66
+ @authorizer ||= options.fetch(:authorizer, Google::Auth::ServiceAccountCredentials).make_creds(
67
+ json_key_io: StringIO.new(credentials.to_json),
68
+ scope: "https://www.googleapis.com/auth/firebase.messaging"
69
+ )
70
+ end
71
+
72
+ def format(device_token)
73
+ notification.send(options[:format], device_token)
74
+ end
75
+
76
+ def device_tokens
77
+ if notification.respond_to?(:fcm_device_tokens)
78
+ Array.wrap(notification.fcm_device_tokens(recipient))
79
+ else
80
+ raise NoMethodError, <<~MESSAGE
81
+ You must implement `fcm_device_tokens` to send Firebase Cloud Messaging notifications
82
+
83
+ # This must return an Array of FCM device tokens
84
+ def fcm_device_tokens(recipient)
85
+ recipient.fcm_device_tokens.pluck(:token)
86
+ end
87
+ MESSAGE
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -51,8 +51,8 @@ module Noticed
51
51
  You must implement `ios_device_tokens` to send iOS notifications
52
52
 
53
53
  # This must return an Array of iOS device tokens
54
- def ios_device_tokens(user)
55
- user.ios_device_tokens.pluck(:token)
54
+ def ios_device_tokens(recipient)
55
+ recipient.ios_device_tokens.pluck(:token)
56
56
  end
57
57
  MESSAGE
58
58
  end
@@ -1,3 +1,3 @@
1
1
  module Noticed
2
- VERSION = "1.5.5"
2
+ VERSION = "1.5.6"
3
3
  end
data/lib/noticed.rb CHANGED
@@ -22,6 +22,7 @@ module Noticed
22
22
  autoload :Test, "noticed/delivery_methods/test"
23
23
  autoload :Twilio, "noticed/delivery_methods/twilio"
24
24
  autoload :Vonage, "noticed/delivery_methods/vonage"
25
+ autoload :Fcm, "noticed/delivery_methods/fcm"
25
26
  end
26
27
 
27
28
  def self.notify(recipients:, notification:)
@@ -1,11 +1,11 @@
1
1
  # The following implements polyfills for Rails < 6.0
2
2
  module ActionCable
3
3
  # If the Rails 6.0 ActionCable::TestHelper is missing then allow it to autoload
4
- unless ActionCable.const_defined? "TestHelper"
4
+ unless ActionCable.const_defined? :TestHelper
5
5
  autoload :TestHelper, "rails_6_polyfills/actioncable/test_helper.rb"
6
6
  end
7
7
  # If the Rails 6.0 test SubscriptionAdapter is missing then allow it to autoload
8
- unless ActionCable.const_defined? "SubscriptionAdapter::Test"
8
+ unless ActionCable::SubscriptionAdapter.const_defined? :Test
9
9
  module SubscriptionAdapter
10
10
  autoload :Test, "rails_6_polyfills/actioncable/test_adapter.rb"
11
11
  end
@@ -13,6 +13,6 @@ module ActionCable
13
13
  end
14
14
 
15
15
  # If the Rails 6.0 ActionJob Serializers are missing then load support for them
16
- unless Object.const_defined?("ActiveJob::Serializers")
16
+ unless ActiveJob.const_defined?(:Serializers)
17
17
  require "rails_6_polyfills/activejob/serializers"
18
18
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: noticed
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.5
4
+ version: 1.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Oliver
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-01 00:00:00.000000000 Z
11
+ date: 2022-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -132,6 +132,7 @@ files:
132
132
  - lib/noticed/delivery_methods/base.rb
133
133
  - lib/noticed/delivery_methods/database.rb
134
134
  - lib/noticed/delivery_methods/email.rb
135
+ - lib/noticed/delivery_methods/fcm.rb
135
136
  - lib/noticed/delivery_methods/ios.rb
136
137
  - lib/noticed/delivery_methods/microsoft_teams.rb
137
138
  - lib/noticed/delivery_methods/slack.rb
@@ -169,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
170
  - !ruby/object:Gem::Version
170
171
  version: '0'
171
172
  requirements: []
172
- rubygems_version: 3.2.22
173
+ rubygems_version: 3.2.32
173
174
  signing_key:
174
175
  specification_version: 4
175
176
  summary: Notifications for Ruby on Rails applications