rpush 7.0.1 → 8.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/CHANGELOG.md +28 -4
- 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 +13 -0
- 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 +2 -2
- 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
@@ -1,5 +1,33 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [Unreleased](https://github.com/rpush/rpush/tree/HEAD)
|
4
|
+
|
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
|
+
|
3
31
|
## [v7.0.1](https://github.com/rpush/rpush/tree/v7.0.1) (2022-03-02)
|
4
32
|
|
5
33
|
[Full Changelog](https://github.com/rpush/rpush/compare/v7.0.0...v7.0.1)
|
@@ -8,10 +36,6 @@
|
|
8
36
|
|
9
37
|
- Fix deprecation warnings from the redis gem [\#636](https://github.com/rpush/rpush/pull/636) ([sharang-d](https://github.com/sharang-d))
|
10
38
|
|
11
|
-
## [Unreleased](https://github.com/rpush/rpush/tree/HEAD)
|
12
|
-
|
13
|
-
[Full Changelog](https://github.com/rpush/rpush/compare/v7.0.0...HEAD)
|
14
|
-
|
15
39
|
## [v7.0.0](https://github.com/rpush/rpush/tree/HEAD)
|
16
40
|
|
17
41
|
[Full Changelog](https://github.com/rpush/rpush/compare/v6.0.1...v7.0.0)
|
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
|