rpush 7.0.0 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -1
- data/README.md +38 -1
- data/lib/generators/rpush_migration_generator.rb +1 -0
- data/lib/generators/templates/rpush.rb +17 -0
- data/lib/generators/templates/rpush_7_1_0_updates.rb +12 -0
- data/lib/rpush/client/active_model/apns/notification.rb +0 -4
- data/lib/rpush/client/active_model/fcm/app.rb +20 -0
- data/lib/rpush/client/active_model/fcm/expiry_collapse_key_mutual_inclusion_validator.rb +14 -0
- data/lib/rpush/client/active_model/fcm/notification.rb +129 -0
- data/lib/rpush/client/active_model/fcm/notification_keys_in_allowed_list_validator.rb +20 -0
- data/lib/rpush/client/active_model.rb +5 -0
- data/lib/rpush/client/active_record/fcm/app.rb +11 -0
- data/lib/rpush/client/active_record/fcm/notification.rb +11 -0
- data/lib/rpush/client/active_record.rb +3 -0
- data/lib/rpush/client/redis/app.rb +2 -0
- data/lib/rpush/client/redis/fcm/app.rb +11 -0
- data/lib/rpush/client/redis/fcm/notification.rb +11 -0
- data/lib/rpush/client/redis.rb +3 -0
- data/lib/rpush/configuration.rb +1 -1
- data/lib/rpush/daemon/apns2/delivery.rb +0 -1
- data/lib/rpush/daemon/apnsp8/delivery.rb +0 -1
- data/lib/rpush/daemon/fcm/delivery.rb +162 -0
- data/lib/rpush/daemon/fcm.rb +9 -0
- data/lib/rpush/daemon/gcm/delivery.rb +1 -1
- data/lib/rpush/daemon/google_credential_cache.rb +41 -0
- data/lib/rpush/daemon/store/active_record.rb +15 -0
- data/lib/rpush/daemon/store/interface.rb +2 -2
- data/lib/rpush/daemon/store/redis.rb +19 -6
- data/lib/rpush/daemon/webpush/delivery.rb +2 -2
- data/lib/rpush/daemon.rb +4 -0
- data/lib/rpush/reflection_collection.rb +3 -2
- data/lib/rpush/version.rb +1 -1
- data/lib/rpush.rb +1 -0
- data/spec/functional/apns2_spec.rb +2 -6
- data/spec/functional/fcm_priority_spec.rb +46 -0
- data/spec/functional/fcm_spec.rb +77 -0
- data/spec/functional_spec_helper.rb +1 -1
- data/spec/spec_helper.rb +2 -1
- data/spec/support/active_record_setup.rb +3 -1
- data/spec/unit/client/active_record/fcm/app_spec.rb +6 -0
- data/spec/unit/client/active_record/fcm/notification_spec.rb +10 -0
- data/spec/unit/client/redis/fcm/app_spec.rb +5 -0
- data/spec/unit/client/redis/fcm/notification_spec.rb +5 -0
- data/spec/unit/client/shared/apns/notification.rb +0 -15
- data/spec/unit/client/shared/fcm/app.rb +4 -0
- data/spec/unit/client/shared/fcm/notification.rb +92 -0
- data/spec/unit/configuration_spec.rb +1 -1
- data/spec/unit/daemon/apnsp8/delivery_spec.rb +1 -1
- data/spec/unit/daemon/fcm/delivery_spec.rb +127 -0
- data/spec/unit/daemon/service_config_methods_spec.rb +1 -1
- data/spec/unit/daemon/tcp_connection_spec.rb +8 -7
- data/spec/unit/daemon/wns/delivery_spec.rb +1 -1
- data/spec/unit/logger_spec.rb +1 -1
- data/spec/unit_spec_helper.rb +1 -1
- metadata +81 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 563933a59960a39b083c6fa55d8300d1b92c29e332cb7ecded294ecc772965ef
|
4
|
+
data.tar.gz: dd9f6cba0c315d7398266a00a1c59cd121ad0167f1217e7781acb402d5ee12e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab9641db8c6e0b0f77b1655d00082d29abb8ef0e83eadc640460abbad6624c96b1641501b597de20cc8c5c9dc8d39ecc34673ad174e6e42f0e036168822c36f9
|
7
|
+
data.tar.gz: 6218b33d044790b48cc8f14b4d3d43cbef90a1189daf52b248db2052c45d36120b946412e449212726d63ccb8392640f2cebe4bc0d02c904aa688ae27cec3354
|
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,39 @@
|
|
2
2
|
|
3
3
|
## [Unreleased](https://github.com/rpush/rpush/tree/HEAD)
|
4
4
|
|
5
|
-
|
5
|
+
**Merged pull requests:**
|
6
|
+
|
7
|
+
* Support for FCMv1 [\#620](https://github.com/rpush/rpush/pull/620) ([mirkode](https://github.com/mirkode)), [\#660](https://github.com/rpush/rpush/pull/660) ([AnilRh](https://github.com/AnilRh)) and [\#673](https://github.com/rpush/rpush/pull/673) ([SixiS](https://github.com/SixiS), [Henridv](https://github.com/Henridv) & [benlangfeld](https://github.com/benlangfeld))
|
8
|
+
* No longer silence content-available notifications for APNs. Reverts the following change from the v6.0.0 release. See https://github.com/rpush/rpush/issues/647.
|
9
|
+
* Fix silent APNS notifications for Apns2 and Apnsp8 [\#596](https://github.com/rpush/rpush/pull/596) ([shved270189](https://github.com/shved270189))
|
10
|
+
|
11
|
+
**Breaking:**
|
12
|
+
|
13
|
+
* Dropped support for Ruby 2.4, 2.5, 2.6 and Rails 5.2.
|
14
|
+
|
15
|
+
[Full Changelog](https://github.com/rpush/rpush/compare/v8.0.0...HEAD)
|
16
|
+
|
17
|
+
## [v7.0.1](https://github.com/rpush/rpush/tree/v8.0.0) (2024-09-06)
|
18
|
+
|
19
|
+
**Merged pull requests:**
|
20
|
+
|
21
|
+
* Support for FCMv1 [\#620](https://github.com/rpush/rpush/pull/620) ([mirkode](https://github.com/mirkode)), [\#660](https://github.com/rpush/rpush/pull/660) ([AnilRh](https://github.com/AnilRh)) and [\#673](https://github.com/rpush/rpush/pull/673) ([SixiS](https://github.com/SixiS), [Henridv](https://github.com/Henridv) & [benlangfeld](https://github.com/benlangfeld))
|
22
|
+
* No longer silence content-available notifications for APNs. Reverts the following change from the v6.0.0 release. See https://github.com/rpush/rpush/issues/647.
|
23
|
+
* Fix silent APNS notifications for Apns2 and Apnsp8 [\#596](https://github.com/rpush/rpush/pull/596) ([shved270189](https://github.com/shved270189))
|
24
|
+
|
25
|
+
**Breaking:**
|
26
|
+
|
27
|
+
* Dropped support for Ruby 2.4, 2.5, 2.6 and Rails 5.2.
|
28
|
+
|
29
|
+
[Full Changelog](https://github.com/rpush/rpush/compare/v7.0.1...v8.0.0)
|
30
|
+
|
31
|
+
## [v7.0.1](https://github.com/rpush/rpush/tree/v7.0.1) (2022-03-02)
|
32
|
+
|
33
|
+
[Full Changelog](https://github.com/rpush/rpush/compare/v7.0.0...v7.0.1)
|
34
|
+
|
35
|
+
**Merged pull requests:**
|
36
|
+
|
37
|
+
- Fix deprecation warnings from the redis gem [\#636](https://github.com/rpush/rpush/pull/636) ([sharang-d](https://github.com/sharang-d))
|
6
38
|
|
7
39
|
## [v7.0.0](https://github.com/rpush/rpush/tree/HEAD)
|
8
40
|
|
data/README.md
CHANGED
@@ -83,6 +83,7 @@ n = Rpush::Apnsp8::Notification.new
|
|
83
83
|
n.app = Rpush::Apnsp8::App.find_by_name("ios_app")
|
84
84
|
n.device_token = "..." # hex string
|
85
85
|
n.alert = "hi mom!"
|
86
|
+
# n.alert = { title: "push title", subtitle: "more to say", body: "hi mom!" }
|
86
87
|
n.data = { foo: :bar }
|
87
88
|
n.save!
|
88
89
|
```
|
@@ -107,6 +108,7 @@ n = Rpush::Apns2::Notification.new
|
|
107
108
|
n.app = Rpush::Apns2::App.find_by_name("ios_app")
|
108
109
|
n.device_token = "..." # hex string
|
109
110
|
n.alert = "hi mom!"
|
111
|
+
# n.alert = { title: "push title", subtitle: "more to say", body: "hi mom!" }
|
110
112
|
n.data = {
|
111
113
|
headers: { 'apns-topic': "BUNDLE ID" }, # the bundle id of the app, like com.example.appname. Not necessary if set on the app (see above)
|
112
114
|
foo: :bar
|
@@ -133,6 +135,7 @@ n = Rpush::Apns::Notification.new
|
|
133
135
|
n.app = Rpush::Apns::App.find_by_name("ios_app")
|
134
136
|
n.device_token = "..." # hex string
|
135
137
|
n.alert = "hi mom!"
|
138
|
+
# n.alert = { title: "push title", subtitle: "more to say", body: "hi mom!" }
|
136
139
|
n.data = { foo: :bar }
|
137
140
|
n.save!
|
138
141
|
```
|
@@ -147,6 +150,40 @@ The app `environment` for any Apns* option is "development" for XCode installs,
|
|
147
150
|
|
148
151
|
#### Firebase Cloud Messaging
|
149
152
|
|
153
|
+
##### Firebase Cloud Messaging API (V1)
|
154
|
+
|
155
|
+
You will need two params to make use of FCM via Rpush.
|
156
|
+
- `firebase_project_id` - The `Project ID` in your Firebase Project Settings
|
157
|
+
- `json_key` - The JSON key file for a service account with the `Firebase Admin SDK Administrator Service Agent` role.
|
158
|
+
|
159
|
+
Create service account in the google cloud account attached to your firebase account:
|
160
|
+
https://console.cloud.google.com/iam-admin/serviceaccounts
|
161
|
+
Make sure it has Role `Firebase Admin SDK Administrator Service Agent`
|
162
|
+
Add + Download the json key for the service account.
|
163
|
+
|
164
|
+
Once you have those two params, you can create an FCM app and send notifications.
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
fcm_app = Rpush::Fcm::App.new
|
168
|
+
fcm_app.name = "fcm_app"
|
169
|
+
fcm_app.firebase_project_id = "someapp-123456"
|
170
|
+
fcm_app.json_key = Rails.root.join("your/key/somewhere.json").read # or from a ENV variable - just needs to be the whole json file
|
171
|
+
fcm_app.connections = 30
|
172
|
+
fcm_app.save!
|
173
|
+
```
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
n = Rpush::Fcm::Notification.new
|
177
|
+
n.app = Rpush::Fcm::App.where(name: "fcm_app").first
|
178
|
+
n.device_token = device_token # Note that device_token is used here instead of registration_ids
|
179
|
+
n.data = {}.transform_values(&:to_s) # All values going in here have to be strings, if you have anything else - nothing goes through
|
180
|
+
n.save!
|
181
|
+
```
|
182
|
+
|
183
|
+
##### Cloud Messaging API (Legacy)
|
184
|
+
|
185
|
+
**Note:** Deprecated on 2023/6/20 and scheduled to be disabled on 2024/6/20.
|
186
|
+
|
150
187
|
FCM and GCM are – as of writing – compatible with each other. See also [this comment](https://github.com/rpush/rpush/issues/284#issuecomment-228330206) for further references.
|
151
188
|
|
152
189
|
Please refer to the Firebase Console on where to find your `auth_key` (probably called _Server Key_ there). To verify you have the right key, use tools like [Postman](https://www.getpostman.com/), [HTTPie](https://httpie.org/), `curl` or similar before reporting a new issue. See also [this comment](https://github.com/rpush/rpush/issues/346#issuecomment-289218776).
|
@@ -470,7 +507,7 @@ This will run RSpec against all versions of Rails.
|
|
470
507
|
You need to specify a `BUNDLE_GEMFILE` pointing to the gemfile before running the normal test command:
|
471
508
|
|
472
509
|
```
|
473
|
-
BUNDLE_GEMFILE=gemfiles/
|
510
|
+
BUNDLE_GEMFILE=gemfiles/rails_6.0.gemfile rspec spec/unit/apns_feedback_spec.rb
|
474
511
|
```
|
475
512
|
|
476
513
|
##### Multiple database adapter support
|
@@ -53,6 +53,7 @@ class RpushMigrationGenerator < Rails::Generators::Base
|
|
53
53
|
add_rpush_migration('rpush_4_1_0_updates')
|
54
54
|
add_rpush_migration('rpush_4_1_1_updates')
|
55
55
|
add_rpush_migration('rpush_4_2_0_updates')
|
56
|
+
add_rpush_migration('rpush_7_1_0_updates')
|
56
57
|
end
|
57
58
|
|
58
59
|
protected
|
@@ -80,6 +80,23 @@ Rpush.reflect do |on|
|
|
80
80
|
# on.tcp_connection_lost do |app, error|
|
81
81
|
# end
|
82
82
|
|
83
|
+
# Called for each recipient which successfully receives a notification. This
|
84
|
+
# can occur more than once for the same notification when there are multiple
|
85
|
+
# recipients.
|
86
|
+
# on.fcm_delivered_to_recipient do |notification|
|
87
|
+
# end
|
88
|
+
|
89
|
+
# Called for each recipient which fails to receive a notification. This
|
90
|
+
# can occur more than once for the same notification when there are multiple
|
91
|
+
# recipients. (do not handle invalid registration IDs here)
|
92
|
+
# on.fcm_failed_to_recipient do |notification, error|
|
93
|
+
# end
|
94
|
+
|
95
|
+
# Called when the FCM returns a failure that indicates an invalid device token.
|
96
|
+
# You will need to delete the device token from your records.
|
97
|
+
# on.fcm_invalid_device_token do |app, error, device_token|
|
98
|
+
# end
|
99
|
+
|
83
100
|
# Called for each recipient which successfully receives a notification. This
|
84
101
|
# can occur more than once for the same notification when there are multiple
|
85
102
|
# recipients.
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Rpush710Updates < ActiveRecord::Migration["#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"]
|
2
|
+
def self.up
|
3
|
+
add_column :rpush_apps, :firebase_project_id, :string
|
4
|
+
add_column :rpush_apps, :json_key, :text
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.down
|
8
|
+
remove_column :rpush_apps, :firebase_project_id
|
9
|
+
remove_column :rpush_apps, :json_key
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -52,10 +52,6 @@ module Rpush
|
|
52
52
|
self.data = (data || {}).merge(CONTENT_AVAILABLE_KEY => true)
|
53
53
|
end
|
54
54
|
|
55
|
-
def content_available?
|
56
|
-
(self.data || {})[CONTENT_AVAILABLE_KEY]
|
57
|
-
end
|
58
|
-
|
59
55
|
def as_json(options = nil) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
60
56
|
json = ActiveSupport::OrderedHash.new
|
61
57
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Client
|
3
|
+
module ActiveModel
|
4
|
+
module Fcm
|
5
|
+
module App
|
6
|
+
def self.included(base)
|
7
|
+
base.instance_eval do
|
8
|
+
# TODO: Add whatever validation is needed here
|
9
|
+
# validates :auth_key, presence: true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def service_name
|
14
|
+
'fcm'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Client
|
3
|
+
module ActiveModel
|
4
|
+
module Fcm
|
5
|
+
class ExpiryCollapseKeyMutualInclusionValidator < ::ActiveModel::Validator
|
6
|
+
def validate(record)
|
7
|
+
return unless record.collapse_key && !record.expiry
|
8
|
+
record.errors.add :expiry, 'must be set when using a collapse_key'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Client
|
3
|
+
module ActiveModel
|
4
|
+
module Fcm
|
5
|
+
module Notification
|
6
|
+
FCM_PRIORITY_HIGH = Rpush::Client::ActiveModel::Apns::Notification::APNS_PRIORITY_IMMEDIATE
|
7
|
+
FCM_PRIORITY_NORMAL = Rpush::Client::ActiveModel::Apns::Notification::APNS_PRIORITY_CONSERVE_POWER
|
8
|
+
FCM_PRIORITIES = [FCM_PRIORITY_HIGH, FCM_PRIORITY_NORMAL]
|
9
|
+
|
10
|
+
ROOT_NOTIFICATION_KEYS = %w[title body image].freeze
|
11
|
+
ANDROID_NOTIFICATION_KEYS = %w[icon tag color click_action body_loc_key body_loc_args title_loc_key
|
12
|
+
title_loc_args channel_id ticker sticky event_time local_only
|
13
|
+
default_vibrate_timings default_light_settings vibrate_timings
|
14
|
+
visibility notification_count light_settings].freeze
|
15
|
+
|
16
|
+
def self.included(base)
|
17
|
+
base.instance_eval do
|
18
|
+
validates :device_token, presence: true
|
19
|
+
validates :priority, inclusion: { in: FCM_PRIORITIES }, allow_nil: true
|
20
|
+
|
21
|
+
validates_with Rpush::Client::ActiveModel::PayloadDataSizeValidator, limit: 4096
|
22
|
+
validates_with Rpush::Client::ActiveModel::RegistrationIdsCountValidator, limit: 1000
|
23
|
+
|
24
|
+
validates_with Rpush::Client::ActiveModel::Fcm::ExpiryCollapseKeyMutualInclusionValidator
|
25
|
+
validates_with Rpush::Client::ActiveModel::Fcm::NotificationKeysInAllowedListValidator
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def payload_data_size
|
30
|
+
multi_json_dump(as_json['message']['data']).bytesize
|
31
|
+
end
|
32
|
+
|
33
|
+
# This is a hack. The schema defines `priority` to be an integer, but FCM expects a string.
|
34
|
+
# But for users of rpush to have an API they might expect (setting priority to `high`, not 10)
|
35
|
+
# we do a little conversion here.
|
36
|
+
def priority=(priority)
|
37
|
+
case priority
|
38
|
+
when 'high', FCM_PRIORITY_HIGH
|
39
|
+
super(FCM_PRIORITY_HIGH)
|
40
|
+
when 'normal', FCM_PRIORITY_NORMAL
|
41
|
+
super(FCM_PRIORITY_NORMAL)
|
42
|
+
else
|
43
|
+
errors.add(:priority, 'must be one of either "normal" or "high"')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def dry_run=(value)
|
48
|
+
fail ArgumentError, 'FCM does not support dry run' if value
|
49
|
+
end
|
50
|
+
|
51
|
+
def as_json(options = nil) # rubocop:disable Metrics/PerceivedComplexity
|
52
|
+
json = {
|
53
|
+
'data' => data,
|
54
|
+
'android' => android_config,
|
55
|
+
'apns' => apns_config,
|
56
|
+
'token' => device_token
|
57
|
+
}
|
58
|
+
|
59
|
+
json['notification'] = root_notification if notification
|
60
|
+
{ 'message' => json }
|
61
|
+
end
|
62
|
+
|
63
|
+
def android_config
|
64
|
+
json = ActiveSupport::OrderedHash.new
|
65
|
+
json['notification'] = android_notification if notification
|
66
|
+
json['collapse_key'] = collapse_key if collapse_key
|
67
|
+
json['priority'] = priority_str if priority
|
68
|
+
json['ttl'] = "#{expiry}s" if expiry
|
69
|
+
json
|
70
|
+
end
|
71
|
+
|
72
|
+
def apns_config
|
73
|
+
json = ActiveSupport::OrderedHash.new
|
74
|
+
json['payload'] = ActiveSupport::OrderedHash.new
|
75
|
+
|
76
|
+
aps = ActiveSupport::OrderedHash.new
|
77
|
+
aps['mutable-content'] = 1 if mutable_content
|
78
|
+
aps['content-available'] = 1 if content_available
|
79
|
+
aps['sound'] = 'default' if sound == 'default'
|
80
|
+
|
81
|
+
json['payload']['aps'] = aps
|
82
|
+
|
83
|
+
json
|
84
|
+
end
|
85
|
+
|
86
|
+
def notification=(value)
|
87
|
+
value = value.with_indifferent_access if value.is_a?(Hash)
|
88
|
+
super(value)
|
89
|
+
end
|
90
|
+
|
91
|
+
def root_notification
|
92
|
+
return {} unless notification
|
93
|
+
|
94
|
+
notification.slice(*ROOT_NOTIFICATION_KEYS)
|
95
|
+
end
|
96
|
+
|
97
|
+
def android_notification
|
98
|
+
json = notification&.slice(*ANDROID_NOTIFICATION_KEYS) || {}
|
99
|
+
json['notification_priority'] = priority_for_notification if priority
|
100
|
+
json['sound'] = sound if sound
|
101
|
+
json['default_sound'] = sound == 'default' ? true : false
|
102
|
+
json
|
103
|
+
end
|
104
|
+
|
105
|
+
def priority_str
|
106
|
+
case
|
107
|
+
when priority <= 5 then 'normal'
|
108
|
+
else
|
109
|
+
'high'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def priority_for_notification
|
114
|
+
case priority
|
115
|
+
when 0 then 'PRIORITY_UNSPECIFIED'
|
116
|
+
when 1 then 'PRIORITY_MIN'
|
117
|
+
when 2 then 'PRIORITY_LOW'
|
118
|
+
when 5 then 'PRIORITY_DEFAULT'
|
119
|
+
when 6 then 'PRIORITY_HIGH'
|
120
|
+
when 10 then 'PRIORITY_MAX'
|
121
|
+
else
|
122
|
+
'PRIORITY_DEFAULT'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Client
|
3
|
+
module ActiveModel
|
4
|
+
module Fcm
|
5
|
+
class NotificationKeysInAllowedListValidator < ::ActiveModel::Validator
|
6
|
+
def validate(record)
|
7
|
+
return unless record.notification
|
8
|
+
|
9
|
+
allowed_keys = Notification::ROOT_NOTIFICATION_KEYS + Notification::ANDROID_NOTIFICATION_KEYS
|
10
|
+
invalid_keys = record.notification.keys - allowed_keys
|
11
|
+
|
12
|
+
return if invalid_keys.empty?
|
13
|
+
|
14
|
+
record.errors.add(:notification, "contains invalid keys: #{invalid_keys.join(', ')}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -20,6 +20,11 @@ require 'rpush/client/active_model/adm/data_validator'
|
|
20
20
|
require 'rpush/client/active_model/adm/app'
|
21
21
|
require 'rpush/client/active_model/adm/notification'
|
22
22
|
|
23
|
+
require 'rpush/client/active_model/fcm/expiry_collapse_key_mutual_inclusion_validator'
|
24
|
+
require 'rpush/client/active_model/fcm/notification_keys_in_allowed_list_validator'
|
25
|
+
require 'rpush/client/active_model/fcm/app'
|
26
|
+
require 'rpush/client/active_model/fcm/notification'
|
27
|
+
|
23
28
|
require 'rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator'
|
24
29
|
require 'rpush/client/active_model/gcm/app'
|
25
30
|
require 'rpush/client/active_model/gcm/notification'
|
@@ -16,6 +16,9 @@ require 'rpush/client/active_record/apns2/app'
|
|
16
16
|
require 'rpush/client/active_record/apnsp8/notification'
|
17
17
|
require 'rpush/client/active_record/apnsp8/app'
|
18
18
|
|
19
|
+
require 'rpush/client/active_record/fcm/notification'
|
20
|
+
require 'rpush/client/active_record/fcm/app'
|
21
|
+
|
19
22
|
require 'rpush/client/active_record/gcm/notification'
|
20
23
|
require 'rpush/client/active_record/gcm/app'
|
21
24
|
|
data/lib/rpush/client/redis.rb
CHANGED
@@ -27,6 +27,9 @@ require 'rpush/client/redis/apns2/notification'
|
|
27
27
|
require 'rpush/client/redis/apnsp8/app'
|
28
28
|
require 'rpush/client/redis/apnsp8/notification'
|
29
29
|
|
30
|
+
require 'rpush/client/redis/fcm/app'
|
31
|
+
require 'rpush/client/redis/fcm/notification'
|
32
|
+
|
30
33
|
require 'rpush/client/redis/gcm/app'
|
31
34
|
require 'rpush/client/redis/gcm/notification'
|
32
35
|
|
data/lib/rpush/configuration.rb
CHANGED
@@ -106,7 +106,7 @@ module Rpush
|
|
106
106
|
client_module = Rpush::Client.const_get(client.to_s.camelize)
|
107
107
|
Rpush.send(:include, client_module) unless Rpush.ancestors.include?(client_module)
|
108
108
|
|
109
|
-
[:Apns, :Gcm, :Wpns, :Wns, :Adm, :Pushy, :Webpush].each do |service|
|
109
|
+
[:Apns, :Fcm, :Gcm, :Wpns, :Wns, :Adm, :Pushy, :Webpush].each do |service|
|
110
110
|
Rpush.const_set(service, client_module.const_get(service)) unless Rpush.const_defined?(service)
|
111
111
|
end
|
112
112
|
|
@@ -112,7 +112,6 @@ module Rpush
|
|
112
112
|
headers['apns-expiration'] = '0'
|
113
113
|
headers['apns-priority'] = '10'
|
114
114
|
headers['apns-topic'] = @app.bundle_id
|
115
|
-
headers['apns-push-type'] = 'background' if notification.content_available?
|
116
115
|
|
117
116
|
headers.merge notification_data(notification)[HTTP2_HEADERS_KEY] || {}
|
118
117
|
end
|
@@ -149,7 +149,6 @@ module Rpush
|
|
149
149
|
headers['apns-priority'] = '10'
|
150
150
|
headers['apns-topic'] = @app.bundle_id
|
151
151
|
headers['authorization'] = "bearer #{jwt_token}"
|
152
|
-
headers['apns-push-type'] = 'background' if notification.content_available?
|
153
152
|
|
154
153
|
headers.merge notification_data(notification)[HTTP2_HEADERS_KEY] || {}
|
155
154
|
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
module Fcm
|
4
|
+
# https://firebase.google.com/docs/cloud-messaging/server
|
5
|
+
class Delivery < Rpush::Daemon::Delivery
|
6
|
+
include MultiJsonHelper
|
7
|
+
|
8
|
+
HOST = 'https://fcm.googleapis.com'.freeze
|
9
|
+
SCOPE = 'https://www.googleapis.com/auth/firebase.messaging'.freeze
|
10
|
+
|
11
|
+
def initialize(app, http, notification, batch)
|
12
|
+
if necessary_data_exists?(app)
|
13
|
+
@app = app
|
14
|
+
@http = http
|
15
|
+
@notification = notification
|
16
|
+
@batch = batch
|
17
|
+
|
18
|
+
@uri = URI.parse("#{HOST}/v1/projects/#{@app.firebase_project_id || ENV['FIREBASE_PROJECT_ID']}/messages:send")
|
19
|
+
else
|
20
|
+
Rpush.logger.error("Cannot find necessary configuration! Please make sure you have set all necessary ENV variables or firebase_project_id and json_key attributes.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def perform
|
25
|
+
handle_response(do_post)
|
26
|
+
rescue SocketError => error
|
27
|
+
mark_retryable(@notification, Time.now + 10.seconds, error)
|
28
|
+
raise
|
29
|
+
rescue StandardError => error
|
30
|
+
mark_failed(error)
|
31
|
+
raise
|
32
|
+
ensure
|
33
|
+
@batch.notification_processed
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def handle_response(response)
|
39
|
+
case response.code.to_i
|
40
|
+
when 200
|
41
|
+
ok
|
42
|
+
when 400
|
43
|
+
bad_request(response)
|
44
|
+
when 401
|
45
|
+
unauthorized
|
46
|
+
when 403
|
47
|
+
sender_id_mismatch
|
48
|
+
when 404
|
49
|
+
unregistered(response)
|
50
|
+
when 429
|
51
|
+
too_many_requests
|
52
|
+
when 500
|
53
|
+
internal_server_error(response)
|
54
|
+
when 502
|
55
|
+
bad_gateway(response)
|
56
|
+
when 503
|
57
|
+
service_unavailable(response)
|
58
|
+
when 500..599
|
59
|
+
other_5xx_error(response)
|
60
|
+
else
|
61
|
+
fail Rpush::DeliveryError.new(response.code.to_i, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def ok
|
66
|
+
reflect(:fcm_delivered_to_recipient, @notification)
|
67
|
+
mark_delivered
|
68
|
+
log_info("#{@notification.id} sent to #{@notification.device_token}")
|
69
|
+
end
|
70
|
+
|
71
|
+
def bad_request(response)
|
72
|
+
fail Rpush::DeliveryError.new(400, @notification.id, "FCM failed to handle the JSON request. (#{parse_error(response)})")
|
73
|
+
end
|
74
|
+
|
75
|
+
def unauthorized
|
76
|
+
fail Rpush::DeliveryError.new(401, @notification.id, 'Unauthorized, Bearer token could not be validated.')
|
77
|
+
end
|
78
|
+
|
79
|
+
def sender_id_mismatch
|
80
|
+
fail Rpush::DeliveryError.new(403, @notification.id, 'The sender ID was mismatched. It seems the device token is wrong.')
|
81
|
+
end
|
82
|
+
|
83
|
+
def unregistered(response)
|
84
|
+
error = parse_error(response)
|
85
|
+
reflect(:fcm_invalid_device_token, @app, error, @notification.device_token)
|
86
|
+
fail Rpush::DeliveryError.new(404, @notification.id, "Client was not registered for your app. (#{error})")
|
87
|
+
end
|
88
|
+
|
89
|
+
def too_many_requests
|
90
|
+
fail Rpush::DeliveryError.new(429, @notification.id, 'Slow down. Too many requests were sent!')
|
91
|
+
end
|
92
|
+
|
93
|
+
def internal_server_error(response)
|
94
|
+
retry_delivery(@notification, response)
|
95
|
+
log_warn("FCM responded with an Internal Error. " + retry_message)
|
96
|
+
end
|
97
|
+
|
98
|
+
def bad_gateway(response)
|
99
|
+
retry_delivery(@notification, response)
|
100
|
+
log_warn("FCM responded with a Bad Gateway Error. " + retry_message)
|
101
|
+
end
|
102
|
+
|
103
|
+
def service_unavailable(response)
|
104
|
+
retry_delivery(@notification, response)
|
105
|
+
log_warn("FCM responded with an Service Unavailable Error. " + retry_message)
|
106
|
+
end
|
107
|
+
|
108
|
+
def other_5xx_error(response)
|
109
|
+
retry_delivery(@notification, response)
|
110
|
+
log_warn("FCM responded with a 5xx Error. " + retry_message)
|
111
|
+
end
|
112
|
+
|
113
|
+
def parse_error(response)
|
114
|
+
error = multi_json_load(response.body)['error']
|
115
|
+
"#{error['status']}: #{error['message']}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def deliver_after_header(response)
|
119
|
+
Rpush::Daemon::RetryHeaderParser.parse(response.header['retry-after'])
|
120
|
+
end
|
121
|
+
|
122
|
+
def retry_delivery(notification, response)
|
123
|
+
time = deliver_after_header(response)
|
124
|
+
if time
|
125
|
+
mark_retryable(notification, time)
|
126
|
+
else
|
127
|
+
mark_retryable_exponential(notification)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def retry_message
|
132
|
+
"Notification #{@notification.id} will be retried after #{@notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')} (retry #{@notification.retries})."
|
133
|
+
end
|
134
|
+
|
135
|
+
def obtain_access_token
|
136
|
+
GoogleCredentialCache.instance.access_token(SCOPE, @app.json_key)
|
137
|
+
end
|
138
|
+
|
139
|
+
def do_post
|
140
|
+
token = obtain_access_token['access_token']
|
141
|
+
post = Net::HTTP::Post.new(@uri.path, 'Content-Type' => 'application/json',
|
142
|
+
'Authorization' => "Bearer #{token}")
|
143
|
+
post.body = @notification.as_json.to_json
|
144
|
+
@http.request(@uri, post)
|
145
|
+
end
|
146
|
+
|
147
|
+
def necessary_data_exists?(app)
|
148
|
+
# Needed for Google Auth
|
149
|
+
# See https://github.com/googleapis/google-auth-library-ruby#example-environment-variables
|
150
|
+
# for further information
|
151
|
+
(app.firebase_project_id || ENV.key?('FIREBASE_PROJECT_ID')) &&
|
152
|
+
(app.json_key || (
|
153
|
+
ENV.key?('GOOGLE_ACCOUNT_TYPE') &&
|
154
|
+
ENV.key?('GOOGLE_CLIENT_ID') &&
|
155
|
+
ENV.key?('GOOGLE_CLIENT_EMAIL') &&
|
156
|
+
ENV.key?('GOOGLE_PRIVATE_KEY')
|
157
|
+
))
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|