noticed 1.6.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|