noticed 1.6.2 → 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 +4 -4
- data/README.md +122 -147
- data/app/jobs/noticed/application_job.rb +9 -0
- data/app/jobs/noticed/event_job.rb +19 -0
- data/app/models/concerns/noticed/deliverable.rb +115 -0
- data/app/models/concerns/noticed/notification_methods.rb +17 -0
- data/app/models/concerns/noticed/readable.rb +62 -0
- data/app/models/noticed/application_record.rb +6 -0
- data/app/models/noticed/deliverable/deliver_by.rb +43 -0
- data/app/models/noticed/event.rb +15 -0
- data/app/models/noticed/notification.rb +16 -0
- data/db/migrate/20231215190233_create_noticed_tables.rb +25 -0
- data/lib/generators/noticed/delivery_method_generator.rb +1 -1
- data/lib/generators/noticed/install_generator.rb +19 -0
- data/lib/generators/noticed/{notification_generator.rb → notifier_generator.rb} +2 -2
- data/lib/generators/noticed/templates/README +5 -4
- data/lib/generators/noticed/templates/notifier.rb.tt +24 -0
- data/lib/noticed/api_client.rb +44 -0
- data/lib/noticed/bulk_delivery_method.rb +46 -0
- data/lib/noticed/bulk_delivery_methods/discord.rb +11 -0
- data/lib/noticed/bulk_delivery_methods/slack.rb +17 -0
- data/lib/noticed/bulk_delivery_methods/webhook.rb +18 -0
- data/lib/noticed/coder.rb +2 -0
- data/lib/noticed/delivery_method.rb +50 -0
- data/lib/noticed/delivery_methods/action_cable.rb +7 -39
- data/lib/noticed/delivery_methods/discord.rb +11 -0
- data/lib/noticed/delivery_methods/email.rb +9 -45
- data/lib/noticed/delivery_methods/fcm.rb +23 -64
- data/lib/noticed/delivery_methods/ios.rb +25 -112
- data/lib/noticed/delivery_methods/microsoft_teams.rb +5 -22
- data/lib/noticed/delivery_methods/slack.rb +6 -16
- data/lib/noticed/delivery_methods/test.rb +2 -12
- data/lib/noticed/delivery_methods/twilio_messaging.rb +37 -0
- data/lib/noticed/delivery_methods/vonage_sms.rb +20 -0
- data/lib/noticed/delivery_methods/webhook.rb +17 -0
- data/lib/noticed/engine.rb +1 -9
- data/lib/noticed/required_options.rb +21 -0
- data/lib/noticed/translation.rb +7 -3
- data/lib/noticed/version.rb +1 -1
- data/lib/noticed.rb +30 -15
- metadata +29 -40
- data/lib/generators/noticed/model/base_generator.rb +0 -48
- data/lib/generators/noticed/model/mysql_generator.rb +0 -18
- data/lib/generators/noticed/model/postgresql_generator.rb +0 -18
- data/lib/generators/noticed/model/sqlite3_generator.rb +0 -18
- data/lib/generators/noticed/model_generator.rb +0 -63
- data/lib/generators/noticed/templates/notification.rb.tt +0 -27
- data/lib/noticed/base.rb +0 -160
- data/lib/noticed/delivery_methods/base.rb +0 -93
- data/lib/noticed/delivery_methods/database.rb +0 -34
- data/lib/noticed/delivery_methods/twilio.rb +0 -51
- data/lib/noticed/delivery_methods/vonage.rb +0 -40
- data/lib/noticed/has_notifications.rb +0 -49
- data/lib/noticed/model.rb +0 -85
- data/lib/noticed/notification_channel.rb +0 -15
- data/lib/noticed/text_coder.rb +0 -16
- data/lib/rails_6_polyfills/actioncable/test_adapter.rb +0 -70
- data/lib/rails_6_polyfills/actioncable/test_helper.rb +0 -143
- data/lib/rails_6_polyfills/activejob/serializers.rb +0 -240
- data/lib/rails_6_polyfills/base.rb +0 -18
- data/lib/tasks/noticed_tasks.rake +0 -4
@@ -0,0 +1,43 @@
|
|
1
|
+
module Noticed
|
2
|
+
module Deliverable
|
3
|
+
class DeliverBy
|
4
|
+
attr_reader :name, :config, :bulk
|
5
|
+
|
6
|
+
def initialize(name, config, bulk: false)
|
7
|
+
@name, @config, @bulk, = name, config, bulk
|
8
|
+
end
|
9
|
+
|
10
|
+
def constant
|
11
|
+
namespace = bulk ? "Noticed::BulkDeliveryMethods" : "Noticed::DeliveryMethods"
|
12
|
+
config.fetch(:class, [namespace, name.to_s.camelize].join("::")).constantize
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate!
|
16
|
+
constant.required_option_names.each do |option|
|
17
|
+
raise ValidationError, "option `#{option}` must be set for `deliver_by :#{name}`" unless config[option].present?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def perform_later(event_or_notification, options = {})
|
22
|
+
options[:wait] = evaluate_option(:wait, event_or_notification) if config.has_key?(:wait)
|
23
|
+
options[:wait_until] = evaluate_option(:wait_until, event_or_notification) if config.has_key?(:wait_until)
|
24
|
+
options[:queue] = evaluate_option(:queue, event_or_notification) if config.has_key?(:queue)
|
25
|
+
options[:priority] = evaluate_option(:priority, event_or_notification) if config.has_key?(:priority)
|
26
|
+
|
27
|
+
constant.set(options).perform_later(name, event_or_notification)
|
28
|
+
end
|
29
|
+
|
30
|
+
def evaluate_option(name, context)
|
31
|
+
option = config[name]
|
32
|
+
|
33
|
+
if option&.respond_to?(:call)
|
34
|
+
context.instance_exec(&option)
|
35
|
+
elsif option.is_a?(Symbol) && context.respond_to?(option)
|
36
|
+
context.send(option)
|
37
|
+
else
|
38
|
+
option
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Noticed
|
2
|
+
class Event < ApplicationRecord
|
3
|
+
include Deliverable
|
4
|
+
include NotificationMethods
|
5
|
+
include Translation
|
6
|
+
include Rails.application.routes.url_helpers
|
7
|
+
|
8
|
+
belongs_to :record, polymorphic: true, optional: true
|
9
|
+
has_many :notifications, dependent: :delete_all
|
10
|
+
|
11
|
+
accepts_nested_attributes_for :notifications
|
12
|
+
|
13
|
+
scope :newest_first, -> { order(created_at: :desc) }
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Noticed
|
2
|
+
class Notification < ApplicationRecord
|
3
|
+
include Rails.application.routes.url_helpers
|
4
|
+
include Readable
|
5
|
+
include Translation
|
6
|
+
|
7
|
+
belongs_to :event
|
8
|
+
belongs_to :recipient, polymorphic: true
|
9
|
+
|
10
|
+
scope :newest_first, -> { order(created_at: :desc) }
|
11
|
+
|
12
|
+
delegate :params, :record, to: :event
|
13
|
+
|
14
|
+
attribute :params, default: {}
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class CreateNoticedTables < ActiveRecord::Migration[6.1]
|
2
|
+
def change
|
3
|
+
create_table :noticed_events do |t|
|
4
|
+
t.string :type
|
5
|
+
t.belongs_to :record, polymorphic: true
|
6
|
+
if t.respond_to?(:jsonb)
|
7
|
+
t.jsonb :params
|
8
|
+
else
|
9
|
+
t.json :params
|
10
|
+
end
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :noticed_notifications do |t|
|
16
|
+
t.string :type
|
17
|
+
t.belongs_to :event, null: false
|
18
|
+
t.belongs_to :recipient, polymorphic: true, null: false
|
19
|
+
t.datetime :read_at
|
20
|
+
t.datetime :seen_at
|
21
|
+
|
22
|
+
t.timestamps
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -12,7 +12,7 @@ module Noticed
|
|
12
12
|
desc "Generates a class for a custom delivery method with the given NAME."
|
13
13
|
|
14
14
|
def generate_notification
|
15
|
-
template "delivery_method.rb", "app/
|
15
|
+
template "delivery_method.rb", "app/notifiers/delivery_methods/#{singular_name}.rb"
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Noticed
|
4
|
+
module Generators
|
5
|
+
class ModelGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::ResourceHelpers
|
7
|
+
|
8
|
+
source_root File.expand_path("../templates", __FILE__)
|
9
|
+
|
10
|
+
def create_migrations
|
11
|
+
rails_command "railties:install:migrations FROM=noticed", inline: true
|
12
|
+
end
|
13
|
+
|
14
|
+
def done
|
15
|
+
readme "README" if behavior == :invoke
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -4,7 +4,7 @@ require "rails/generators/named_base"
|
|
4
4
|
|
5
5
|
module Noticed
|
6
6
|
module Generators
|
7
|
-
class
|
7
|
+
class NotifierGenerator < Rails::Generators::NamedBase
|
8
8
|
include Rails::Generators::ResourceHelpers
|
9
9
|
|
10
10
|
source_root File.expand_path("../templates", __FILE__)
|
@@ -12,7 +12,7 @@ module Noticed
|
|
12
12
|
desc "Generates a notification with the given NAME."
|
13
13
|
|
14
14
|
def generate_notification
|
15
|
-
template "
|
15
|
+
template "notifier.rb", "app/notifiers/#{file_path}.rb"
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
|
2
|
-
🚚
|
2
|
+
🚚 You're ready to start sending notifications!
|
3
3
|
|
4
4
|
Next steps:
|
5
|
-
1. Run
|
6
|
-
2. Add
|
7
|
-
|
5
|
+
1. Run `rails db:migrate`
|
6
|
+
2. Add `has_many :notifications, as: :recipient, dependent: :destroy, class_name: "Noticed::Notification"` to your User model(s).
|
7
|
+
2. Add `has_many :notifications, as: :record, dependent: :destroy, class_name: "Noticed::Event"` to your model(s) that notifications reference.
|
8
|
+
3. Generate notifiers with "rails g noticed:notifier"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# To deliver this notification:
|
2
|
+
#
|
3
|
+
# <%= class_name %>.with(record: @post, message: "New post").deliver(User.all)
|
4
|
+
|
5
|
+
class <%= class_name %> < Noticed::Event
|
6
|
+
# Add your delivery methods
|
7
|
+
#
|
8
|
+
# deliver_by :email do |config|
|
9
|
+
# config.mailer = "UserMailer"
|
10
|
+
# config.method = "new_post"
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# bulk_deliver_by :slack do |config|
|
14
|
+
# config.url = -> { Rails.application.credentials.slack_webhook_url }
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# deliver_by :custom do |config|
|
18
|
+
# config.class = "MyDeliveryMethod"
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Add required params
|
22
|
+
#
|
23
|
+
# required_param :message
|
24
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "net/http"
|
2
|
+
|
3
|
+
module Noticed
|
4
|
+
module ApiClient
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# Helper method for making POST requests from delivery methods
|
8
|
+
#
|
9
|
+
# Usage:
|
10
|
+
# post_request("http://example.com", basic_auth: {user:, pass:}, headers: {}, json: {}, form: {})
|
11
|
+
#
|
12
|
+
def post_request(url, args = {})
|
13
|
+
args.compact!
|
14
|
+
|
15
|
+
uri = URI(url)
|
16
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
17
|
+
http.use_ssl = true if uri.instance_of? URI::HTTPS
|
18
|
+
|
19
|
+
headers = args.delete(:headers) || {}
|
20
|
+
headers["Content-Type"] = "application/json" if args.has_key?(:json)
|
21
|
+
|
22
|
+
request = Net::HTTP::Post.new(uri.request_uri, headers)
|
23
|
+
|
24
|
+
if (basic_auth = args.delete(:basic_auth))
|
25
|
+
request.basic_auth basic_auth.fetch(:user), basic_auth.fetch(:pass)
|
26
|
+
end
|
27
|
+
|
28
|
+
if (json = args.delete(:json))
|
29
|
+
request.body = json.to_json
|
30
|
+
elsif (form = args.delete(:form))
|
31
|
+
request.set_form(form, "multipart/form-data")
|
32
|
+
end
|
33
|
+
|
34
|
+
logger.debug("POST #{url}")
|
35
|
+
logger.debug(request.body)
|
36
|
+
response = http.request(request)
|
37
|
+
logger.debug("Response: #{response.code}: #{response.body.inspect}")
|
38
|
+
|
39
|
+
raise ResponseUnsuccessful.new(response, url, args) unless response.code.start_with?("20")
|
40
|
+
|
41
|
+
response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Noticed
|
2
|
+
class BulkDeliveryMethod < ApplicationJob
|
3
|
+
include ApiClient
|
4
|
+
include RequiredOptions
|
5
|
+
|
6
|
+
class_attribute :logger, default: Rails.logger
|
7
|
+
|
8
|
+
attr_reader :config, :event
|
9
|
+
|
10
|
+
def perform(delivery_method_name, event)
|
11
|
+
@event = event
|
12
|
+
@config = event.bulk_delivery_methods.fetch(delivery_method_name).config
|
13
|
+
|
14
|
+
return false if config.has_key?(:if) && !evaluate_option(:if)
|
15
|
+
return false if config.has_key?(:unless) && evaluate_option(:unless)
|
16
|
+
|
17
|
+
deliver
|
18
|
+
end
|
19
|
+
|
20
|
+
def deliver
|
21
|
+
raise NotImplementedError, "Bulk delivery methods must implement the `deliver` method"
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch_constant(name)
|
25
|
+
option = config[name]
|
26
|
+
option.is_a?(String) ? option.constantize : option
|
27
|
+
end
|
28
|
+
|
29
|
+
def evaluate_option(name)
|
30
|
+
option = config[name]
|
31
|
+
|
32
|
+
# Evaluate Proc within the context of the notifier
|
33
|
+
if option&.respond_to?(:call)
|
34
|
+
event.instance_exec(&option)
|
35
|
+
|
36
|
+
# Call method if symbol and matching method
|
37
|
+
elsif option.is_a?(Symbol) && event.respond_to?(option)
|
38
|
+
event.send(option)
|
39
|
+
|
40
|
+
# Return the value
|
41
|
+
else
|
42
|
+
option
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Noticed
|
2
|
+
module BulkDeliveryMethods
|
3
|
+
class Slack < BulkDeliveryMethod
|
4
|
+
DEFAULT_URL = "https://slack.com/api/chat.postMessage"
|
5
|
+
|
6
|
+
required_options :json
|
7
|
+
|
8
|
+
def deliver
|
9
|
+
post_request url, headers: evaluate_option(:headers), json: evaluate_option(:json)
|
10
|
+
end
|
11
|
+
|
12
|
+
def url
|
13
|
+
evaluate_option(:url) || DEFAULT_URL
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Noticed
|
2
|
+
module BulkDeliveryMethods
|
3
|
+
class Webhook < BulkDeliveryMethod
|
4
|
+
required_options :url
|
5
|
+
|
6
|
+
def deliver
|
7
|
+
Rails.logger.debug(evaluate_option(:json))
|
8
|
+
post_request(
|
9
|
+
evaluate_option(:url),
|
10
|
+
basic_auth: evaluate_option(:basic_auth),
|
11
|
+
headers: evaluate_option(:headers),
|
12
|
+
json: evaluate_option(:json),
|
13
|
+
form: evaluate_option(:form)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/noticed/coder.rb
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Noticed
|
2
|
+
class DeliveryMethod < ApplicationJob
|
3
|
+
include ApiClient
|
4
|
+
include RequiredOptions
|
5
|
+
|
6
|
+
class_attribute :logger, default: Rails.logger
|
7
|
+
|
8
|
+
attr_reader :config, :event, :notification
|
9
|
+
delegate :recipient, to: :notification
|
10
|
+
|
11
|
+
def perform(delivery_method_name, notification, overrides: {})
|
12
|
+
@notification = notification
|
13
|
+
@event = notification.event
|
14
|
+
|
15
|
+
# Look up config from Notifier and merge overrides
|
16
|
+
@config = event.delivery_methods.fetch(delivery_method_name).config.merge(overrides)
|
17
|
+
|
18
|
+
return false if config.has_key?(:if) && !evaluate_option(:if)
|
19
|
+
return false if config.has_key?(:unless) && evaluate_option(:unless)
|
20
|
+
|
21
|
+
deliver
|
22
|
+
end
|
23
|
+
|
24
|
+
def deliver
|
25
|
+
raise NotImplementedError, "Delivery methods must implement the `deliver` method"
|
26
|
+
end
|
27
|
+
|
28
|
+
def fetch_constant(name)
|
29
|
+
option = config[name]
|
30
|
+
option.is_a?(String) ? option.constantize : option
|
31
|
+
end
|
32
|
+
|
33
|
+
def evaluate_option(name)
|
34
|
+
option = config[name]
|
35
|
+
|
36
|
+
# Evaluate Proc within the context of the Notification
|
37
|
+
if option&.respond_to?(:call)
|
38
|
+
notification.instance_exec(&option)
|
39
|
+
|
40
|
+
# Call method if symbol and matching method on Notifier
|
41
|
+
elsif option.is_a?(Symbol) && event.respond_to?(option)
|
42
|
+
event.send(option, self)
|
43
|
+
|
44
|
+
# Return the value
|
45
|
+
else
|
46
|
+
option
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,46 +1,14 @@
|
|
1
1
|
module Noticed
|
2
2
|
module DeliveryMethods
|
3
|
-
class ActionCable <
|
4
|
-
|
5
|
-
channel.broadcast_to stream, format
|
6
|
-
end
|
3
|
+
class ActionCable < DeliveryMethod
|
4
|
+
required_options :channel, :stream, :message
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
notification.send(method)
|
13
|
-
else
|
14
|
-
notification.params
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def channel
|
19
|
-
@channel ||= begin
|
20
|
-
value = options[:channel]
|
21
|
-
case value
|
22
|
-
when String
|
23
|
-
value.constantize
|
24
|
-
when Symbol
|
25
|
-
notification.send(value)
|
26
|
-
when Class
|
27
|
-
value
|
28
|
-
else
|
29
|
-
Noticed::NotificationChannel
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
6
|
+
def deliver
|
7
|
+
channel = fetch_constant(:channel)
|
8
|
+
stream = evaluate_option(:stream)
|
9
|
+
message = evaluate_option(:message)
|
33
10
|
|
34
|
-
|
35
|
-
value = options[:stream]
|
36
|
-
case value
|
37
|
-
when String
|
38
|
-
value
|
39
|
-
when Symbol
|
40
|
-
notification.send(value)
|
41
|
-
else
|
42
|
-
recipient
|
43
|
-
end
|
11
|
+
channel.broadcast_to stream, message
|
44
12
|
end
|
45
13
|
end
|
46
14
|
end
|
@@ -1,54 +1,18 @@
|
|
1
1
|
module Noticed
|
2
2
|
module DeliveryMethods
|
3
|
-
class Email <
|
4
|
-
|
3
|
+
class Email < DeliveryMethod
|
4
|
+
required_options :mailer, :method
|
5
5
|
|
6
6
|
def deliver
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
7
|
+
mailer = fetch_constant(:mailer)
|
8
|
+
email = evaluate_option(:method)
|
9
|
+
params = (evaluate_option(:params) || notification&.params || {}).merge(record: notification&.record)
|
10
|
+
args = evaluate_option(:args)
|
15
11
|
|
16
|
-
|
17
|
-
|
18
|
-
# mailer: :my_method - `my_method` should return Class
|
19
|
-
def mailer
|
20
|
-
option = options.fetch(:mailer)
|
21
|
-
case option
|
22
|
-
when String
|
23
|
-
option.constantize
|
24
|
-
when Symbol
|
25
|
-
notification.send(option)
|
26
|
-
else
|
27
|
-
option
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Method should be a symbol
|
32
|
-
#
|
33
|
-
# If notification responds to symbol, call that method and use return value
|
34
|
-
# If notification does not respond to symbol, use the symbol for the mailer method
|
35
|
-
# Otherwise, use the underscored notification class name as the mailer method
|
36
|
-
def mailer_method
|
37
|
-
method_name = options[:method]&.to_sym
|
38
|
-
if method_name.present?
|
39
|
-
notification.respond_to?(method_name) ? notification.send(method_name) : method_name
|
40
|
-
else
|
41
|
-
notification.class.name.underscore
|
42
|
-
end
|
43
|
-
end
|
12
|
+
mail = mailer.with(params)
|
13
|
+
mail = args.present? ? mail.send(email, *args) : mail.send(email)
|
44
14
|
|
45
|
-
|
46
|
-
params = if (method = options[:format])
|
47
|
-
notification.send(method)
|
48
|
-
else
|
49
|
-
notification.params
|
50
|
-
end
|
51
|
-
params.merge(recipient: recipient, record: record)
|
15
|
+
(!!evaluate_option(:enqueue)) ? mail.deliver_later : mail.deliver_now
|
52
16
|
end
|
53
17
|
end
|
54
18
|
end
|
@@ -1,95 +1,54 @@
|
|
1
1
|
require "googleauth"
|
2
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
3
|
module Noticed
|
13
4
|
module DeliveryMethods
|
14
|
-
class Fcm <
|
15
|
-
|
16
|
-
|
17
|
-
option :format
|
5
|
+
class Fcm < DeliveryMethod
|
6
|
+
required_option :credentials, :device_tokens, :json
|
18
7
|
|
19
8
|
def deliver
|
20
|
-
device_tokens.each do |device_token|
|
21
|
-
|
22
|
-
rescue ResponseUnsuccessful => exception
|
23
|
-
if exception.response.code == 404
|
24
|
-
cleanup_invalid_token(device_token)
|
25
|
-
else
|
26
|
-
raise
|
27
|
-
end
|
9
|
+
evaluate_option(:device_tokens).each do |device_token|
|
10
|
+
send_notification device_token
|
28
11
|
end
|
29
12
|
end
|
30
13
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
14
|
+
def send_notification(device_token)
|
15
|
+
post_request("https://fcm.googleapis.com/v1/projects/#{credentials[:project_id]}/messages:send",
|
16
|
+
headers: {authorization: "Bearer #{access_token}"},
|
17
|
+
json: notification.instance_exec(device_token, &config[:json]))
|
18
|
+
rescue Noticed::ResponseUnsuccessful => exception
|
19
|
+
if exception.response.code == "404" && config[:invalid_token]
|
20
|
+
notification.instance_exec(device_token, &config[:invalid_token])
|
21
|
+
else
|
22
|
+
raise
|
23
|
+
end
|
34
24
|
end
|
35
25
|
|
36
26
|
def credentials
|
37
27
|
@credentials ||= begin
|
38
|
-
|
39
|
-
|
28
|
+
value = evaluate_option(:credentials)
|
29
|
+
case value
|
40
30
|
when Hash
|
41
|
-
|
31
|
+
value
|
42
32
|
when Pathname
|
43
|
-
load_json(
|
33
|
+
load_json(value)
|
44
34
|
when String
|
45
|
-
load_json(Rails.root.join(
|
46
|
-
when Symbol
|
47
|
-
notification.send(option)
|
35
|
+
load_json(Rails.root.join(value))
|
48
36
|
else
|
49
|
-
|
37
|
+
raise ArgumentError, "FCM credentials must be a Hash, String, Pathname, or Symbol"
|
50
38
|
end
|
51
|
-
|
52
|
-
credentials_hash.symbolize_keys
|
53
39
|
end
|
54
40
|
end
|
55
41
|
|
56
42
|
def load_json(path)
|
57
|
-
JSON.parse(File.read(path))
|
58
|
-
end
|
59
|
-
|
60
|
-
def project_id
|
61
|
-
credentials[:project_id]
|
43
|
+
JSON.parse(File.read(path), symbolize_names: true)
|
62
44
|
end
|
63
45
|
|
64
46
|
def access_token
|
65
|
-
|
66
|
-
token["access_token"]
|
67
|
-
end
|
68
|
-
|
69
|
-
def authorizer
|
70
|
-
@authorizer ||= options.fetch(:authorizer, Google::Auth::ServiceAccountCredentials).make_creds(
|
47
|
+
@authorizer ||= (evaluate_option(:authorizer) || Google::Auth::ServiceAccountCredentials).make_creds(
|
71
48
|
json_key_io: StringIO.new(credentials.to_json),
|
72
49
|
scope: "https://www.googleapis.com/auth/firebase.messaging"
|
73
50
|
)
|
74
|
-
|
75
|
-
|
76
|
-
def format(device_token)
|
77
|
-
notification.send(options[:format], device_token)
|
78
|
-
end
|
79
|
-
|
80
|
-
def device_tokens
|
81
|
-
if notification.respond_to?(:fcm_device_tokens)
|
82
|
-
Array.wrap(notification.fcm_device_tokens(recipient))
|
83
|
-
else
|
84
|
-
raise NoMethodError, <<~MESSAGE
|
85
|
-
You must implement `fcm_device_tokens` to send Firebase Cloud Messaging notifications
|
86
|
-
|
87
|
-
# This must return an Array of FCM device tokens
|
88
|
-
def fcm_device_tokens(recipient)
|
89
|
-
recipient.fcm_device_tokens.pluck(:token)
|
90
|
-
end
|
91
|
-
MESSAGE
|
92
|
-
end
|
51
|
+
@authorizer.fetch_access_token!["access_token"]
|
93
52
|
end
|
94
53
|
end
|
95
54
|
end
|