rpush 4.1.1 → 5.3.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 +261 -161
- data/README.md +103 -17
- data/lib/generators/rpush_migration_generator.rb +1 -0
- data/lib/generators/templates/rpush.rb +4 -0
- data/lib/generators/templates/rpush_4_2_0_updates.rb +10 -0
- data/lib/rpush/cli.rb +1 -1
- data/lib/rpush/client/active_model.rb +4 -1
- data/lib/rpush/client/active_model/adm/data_validator.rb +1 -1
- data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +2 -2
- data/lib/rpush/client/active_model/apns/notification.rb +9 -1
- data/lib/rpush/client/active_model/apns/notification_payload_size_validator.rb +15 -0
- data/lib/rpush/client/active_model/apns2/notification.rb +14 -0
- data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +1 -1
- data/lib/rpush/client/active_model/gcm/notification.rb +4 -3
- data/lib/rpush/client/active_model/payload_data_size_validator.rb +1 -1
- data/lib/rpush/client/active_model/registration_ids_count_validator.rb +1 -1
- data/lib/rpush/client/active_model/webpush/app.rb +41 -0
- data/lib/rpush/client/active_model/webpush/notification.rb +66 -0
- data/lib/rpush/client/active_model/wns/notification.rb +8 -0
- data/lib/rpush/client/active_record.rb +4 -0
- data/lib/rpush/client/active_record/apns/active_record_serializable_notification.rb +65 -0
- data/lib/rpush/client/active_record/apns/notification.rb +1 -29
- data/lib/rpush/client/active_record/apns2/notification.rb +4 -1
- data/lib/rpush/client/active_record/apnsp8/notification.rb +1 -0
- data/lib/rpush/client/active_record/app.rb +1 -1
- data/lib/rpush/client/active_record/notification.rb +1 -1
- data/lib/rpush/client/active_record/webpush/app.rb +11 -0
- data/lib/rpush/client/active_record/webpush/notification.rb +12 -0
- data/lib/rpush/client/redis.rb +3 -0
- data/lib/rpush/client/redis/apns2/notification.rb +1 -0
- data/lib/rpush/client/redis/apnsp8/notification.rb +2 -0
- data/lib/rpush/client/redis/notification.rb +2 -1
- data/lib/rpush/client/redis/pushy/notification.rb +0 -1
- data/lib/rpush/client/redis/webpush/app.rb +15 -0
- data/lib/rpush/client/redis/webpush/notification.rb +15 -0
- data/lib/rpush/configuration.rb +3 -2
- data/lib/rpush/daemon.rb +4 -1
- data/lib/rpush/daemon/apns/feedback_receiver.rb +2 -1
- data/lib/rpush/daemon/apns2/delivery.rb +13 -2
- data/lib/rpush/daemon/apnsp8/delivery.rb +7 -2
- data/lib/rpush/daemon/app_runner.rb +1 -1
- data/lib/rpush/daemon/batch.rb +12 -5
- data/lib/rpush/daemon/delivery.rb +1 -2
- data/lib/rpush/daemon/dispatcher/apns_http2.rb +4 -2
- data/lib/rpush/daemon/dispatcher/apnsp8_http2.rb +2 -1
- data/lib/rpush/daemon/signal_handler.rb +1 -1
- data/lib/rpush/daemon/store/active_record.rb +1 -1
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +1 -1
- data/lib/rpush/daemon/store/redis.rb +1 -1
- data/lib/rpush/daemon/webpush.rb +10 -0
- data/lib/rpush/daemon/webpush/delivery.rb +114 -0
- data/lib/rpush/daemon/wns/badge_request.rb +8 -3
- data/lib/rpush/daemon/wns/raw_request.rb +9 -2
- data/lib/rpush/daemon/wns/toast_request.rb +6 -2
- data/lib/rpush/logger.rb +1 -0
- data/lib/rpush/version.rb +3 -3
- data/spec/.rubocop.yml +1 -1
- data/spec/functional/apns2_spec.rb +97 -2
- data/spec/functional/gcm_priority_spec.rb +40 -0
- data/spec/functional/webpush_spec.rb +30 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/active_record_setup.rb +3 -1
- data/spec/support/simplecov_helper.rb +1 -1
- data/spec/unit/client/active_record/adm/app_spec.rb +2 -54
- data/spec/unit/client/active_record/adm/notification_spec.rb +2 -39
- data/spec/unit/client/active_record/apns/app_spec.rb +3 -26
- data/spec/unit/client/active_record/apns/feedback_spec.rb +1 -5
- data/spec/unit/client/active_record/apns/notification_spec.rb +29 -288
- data/spec/unit/client/active_record/apns2/app_spec.rb +4 -0
- data/spec/unit/client/active_record/apns2/notification_spec.rb +65 -0
- data/spec/unit/client/active_record/apnsp8/notification_spec.rb +28 -0
- data/spec/unit/client/active_record/app_spec.rb +1 -26
- data/spec/unit/client/active_record/gcm/app_spec.rb +3 -1
- data/spec/unit/client/active_record/gcm/notification_spec.rb +6 -83
- data/spec/unit/client/active_record/notification_spec.rb +10 -11
- data/spec/unit/client/active_record/pushy/app_spec.rb +2 -13
- data/spec/unit/client/active_record/pushy/notification_spec.rb +2 -55
- data/spec/unit/client/active_record/shared/app.rb +14 -0
- data/spec/unit/{notification_shared.rb → client/active_record/shared/notification.rb} +12 -7
- data/spec/unit/client/active_record/webpush/app_spec.rb +6 -0
- data/spec/unit/client/active_record/webpush/notification_spec.rb +6 -0
- data/spec/unit/client/active_record/wns/badge_notification_spec.rb +1 -11
- data/spec/unit/client/active_record/wns/raw_notification_spec.rb +3 -12
- data/spec/unit/client/active_record/wpns/app_spec.rb +3 -1
- data/spec/unit/client/active_record/wpns/notification_spec.rb +2 -17
- data/spec/unit/client/redis/adm/app_spec.rb +5 -0
- data/spec/unit/client/redis/adm/notification_spec.rb +5 -0
- data/spec/unit/client/redis/apns/app_spec.rb +5 -0
- data/spec/unit/client/redis/apns/feedback_spec.rb +5 -0
- data/spec/unit/client/redis/apns/notification_spec.rb +50 -0
- data/spec/unit/client/redis/apns2/app_spec.rb +4 -0
- data/spec/unit/client/redis/apns2/notification_spec.rb +50 -0
- data/spec/unit/client/redis/apnsp8/notification_spec.rb +29 -0
- data/spec/unit/client/redis/app_spec.rb +5 -0
- data/spec/unit/client/redis/gcm/app_spec.rb +5 -0
- data/spec/unit/client/redis/gcm/notification_spec.rb +5 -0
- data/spec/unit/client/redis/notification_spec.rb +5 -0
- data/spec/unit/client/redis/pushy/app_spec.rb +5 -0
- data/spec/unit/client/redis/pushy/notification_spec.rb +5 -0
- data/spec/unit/client/redis/webpush/app_spec.rb +5 -0
- data/spec/unit/client/redis/webpush/notification_spec.rb +5 -0
- data/spec/unit/client/redis/wns/badge_notification_spec.rb +5 -0
- data/spec/unit/client/redis/wns/raw_notification_spec.rb +22 -0
- data/spec/unit/client/redis/wpns/app_spec.rb +5 -0
- data/spec/unit/client/redis/wpns/notification_spec.rb +5 -0
- data/spec/unit/client/shared/adm/app.rb +51 -0
- data/spec/unit/client/shared/adm/notification.rb +39 -0
- data/spec/unit/client/shared/apns/app.rb +29 -0
- data/spec/unit/client/shared/apns/feedback.rb +9 -0
- data/spec/unit/client/shared/apns/notification.rb +262 -0
- data/spec/unit/client/shared/app.rb +17 -0
- data/spec/unit/client/shared/gcm/app.rb +4 -0
- data/spec/unit/client/shared/gcm/notification.rb +77 -0
- data/spec/unit/client/shared/notification.rb +10 -0
- data/spec/unit/client/shared/pushy/app.rb +17 -0
- data/spec/unit/client/shared/pushy/notification.rb +55 -0
- data/spec/unit/client/shared/webpush/app.rb +33 -0
- data/spec/unit/client/shared/webpush/notification.rb +83 -0
- data/spec/unit/client/shared/wns/badge_notification.rb +15 -0
- data/spec/unit/client/shared/wns/raw_notification.rb +21 -0
- data/spec/unit/client/shared/wpns/app.rb +4 -0
- data/spec/unit/client/shared/wpns/notification.rb +18 -0
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +19 -1
- data/spec/unit/daemon/batch_spec.rb +50 -2
- data/spec/unit/daemon/delivery_spec.rb +10 -0
- data/spec/unit/daemon/gcm/delivery_spec.rb +1 -1
- data/spec/unit/daemon/shared/store.rb +312 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +7 -7
- data/spec/unit/daemon/store/active_record_spec.rb +7 -295
- data/spec/unit/daemon/store/redis_spec.rb +4 -293
- data/spec/unit/daemon/webpush/delivery_spec.rb +142 -0
- data/spec/unit/daemon/wns/post_request_spec.rb +64 -0
- data/spec/unit_spec_helper.rb +3 -0
- metadata +140 -13
- data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +0 -16
data/README.md
CHANGED
@@ -18,10 +18,11 @@ Rpush aims to be the *de facto* gem for sending push notifications in Ruby. Its
|
|
18
18
|
* [**Amazon Device Messaging**](#amazon-device-messaging)
|
19
19
|
* [**Windows Phone Push Notification Service**](#windows-phone-notification-service)
|
20
20
|
* [**Pushy**](#pushy)
|
21
|
+
* [**Webpush**](#webpush)
|
21
22
|
|
22
23
|
#### Feature Highlights
|
23
24
|
|
24
|
-
* Use [**ActiveRecord**](https://github.com/rpush/rpush/wiki/Using-ActiveRecord) or [**Redis**](https://github.com/rpush/rpush/wiki/Using-Redis) for storage.
|
25
|
+
* Use [**ActiveRecord**](https://github.com/rpush/rpush/wiki/Using-ActiveRecord) or [**Redis**](https://github.com/rpush/rpush/wiki/Using-Redis) for storage.
|
25
26
|
* Plugins for [**Bugsnag**](https://github.com/rpush/rpush-plugin-bugsnag),
|
26
27
|
[**Sentry**](https://github.com/rpush/rpush-plugin-sentry), [**StatsD**](https://github.com/rpush/rpush-plugin-statsd) or [write your own](https://github.com/rpush/rpush/wiki/Writing-a-Plugin).
|
27
28
|
* Seamless integration with your projects, including **Rails**.
|
@@ -52,56 +53,98 @@ $ bundle exec rpush init
|
|
52
53
|
|
53
54
|
#### Apple Push Notification Service
|
54
55
|
|
56
|
+
There is a choice of two modes (and one legacy mode) using certificates or using tokens:
|
55
57
|
|
56
|
-
|
58
|
+
* `Rpush::Apns2` This requires an annually renewable certificate. see https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_certificate-based_connection_to_apns
|
59
|
+
* `Rpush::Apnsp8` This uses encrypted tokens and requires an encryption key id and encryption key (provide as a p8 file). (see https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token-based_connection_to_apns)
|
60
|
+
* `Rpush::Apns` There is also the original APNS (the original version using certificates with a binary underlying protocol over TCP directly rather than over Http/2).
|
61
|
+
Apple have [announced](https://developer.apple.com/news/?id=11042019a) that this is not supported after November 2020.
|
62
|
+
|
63
|
+
If this is your first time using the APNs, you will need to generate either SSL certificates (for Apns2 or Apns) or an Encryption Key (p8) and an Encryption Key ID (for Apnsp8). See [Generating Certificates](https://github.com/rpush/rpush/wiki/Generating-Certificates) for instructions.
|
64
|
+
|
65
|
+
##### Apnsp8
|
66
|
+
|
67
|
+
To use the p8 APNs Api:
|
57
68
|
|
58
69
|
```ruby
|
59
|
-
app = Rpush::
|
70
|
+
app = Rpush::Apnsp8::App.new
|
60
71
|
app.name = "ios_app"
|
61
|
-
app.
|
72
|
+
app.apn_key = File.read("/path/to/sandbox.p8")
|
62
73
|
app.environment = "development" # APNs environment.
|
63
|
-
app.
|
74
|
+
app.apn_key_id = "APN KEY ID" # This is the Encryption Key ID provided by apple
|
75
|
+
app.team_id = "TEAM ID" # the team id - e.g. ABCDE12345
|
76
|
+
app.bundle_id = "BUNDLE ID" # the unique bundle id of the app, like com.example.appname
|
64
77
|
app.connections = 1
|
65
78
|
app.save!
|
66
79
|
```
|
67
80
|
|
68
81
|
```ruby
|
69
82
|
n = Rpush::Apns::Notification.new
|
70
|
-
n.app = Rpush::
|
83
|
+
n.app = Rpush::Apnsp8::App.find_by_name("ios_app")
|
71
84
|
n.device_token = "..." # hex string
|
72
85
|
n.alert = "hi mom!"
|
73
86
|
n.data = { foo: :bar }
|
74
87
|
n.save!
|
75
88
|
```
|
76
89
|
|
77
|
-
|
90
|
+
##### Apns2
|
78
91
|
|
79
|
-
|
92
|
+
(NB this uses the same protocol as Apnsp8, but authenticates with a certificate rather than tokens)
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
app = Rpush::Apns2::App.new
|
96
|
+
app.name = "ios_app"
|
97
|
+
app.certificate = File.read("/path/to/sandbox.pem")
|
98
|
+
app.environment = "development"
|
99
|
+
app.password = "certificate password"
|
100
|
+
app.bundle_id = "BUNDLE ID" # the unique bundle id of the app, like com.example.appname
|
101
|
+
app.connections = 1
|
102
|
+
app.save!
|
103
|
+
```
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
n = Rpush::Apns2::Notification.new
|
107
|
+
n.app = Rpush::Apns2::App.find_by_name("ios_app")
|
108
|
+
n.device_token = "..." # hex string
|
109
|
+
n.alert = "hi mom!"
|
110
|
+
n.data = {
|
111
|
+
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
|
+
foo: :bar
|
113
|
+
}
|
114
|
+
n.save!
|
115
|
+
```
|
80
116
|
|
81
|
-
|
117
|
+
You should also implement the [ssl_certificate_will_expire](https://github.com/rpush/rpush/wiki/Reflection-API) reflection to monitor when your certificate is due to expire.
|
82
118
|
|
83
|
-
|
119
|
+
##### Apns (legacy protocol)
|
84
120
|
|
85
121
|
```ruby
|
86
|
-
app = Rpush::
|
122
|
+
app = Rpush::Apns::App.new
|
87
123
|
app.name = "ios_app"
|
88
|
-
app.
|
124
|
+
app.certificate = File.read("/path/to/sandbox.pem")
|
89
125
|
app.environment = "development" # APNs environment.
|
90
|
-
app.
|
91
|
-
app.team_id = "TEAM ID"
|
92
|
-
app.bundle_id = "BUNDLE ID"
|
126
|
+
app.password = "certificate password"
|
93
127
|
app.connections = 1
|
94
128
|
app.save!
|
95
129
|
```
|
96
130
|
|
97
131
|
```ruby
|
98
132
|
n = Rpush::Apns::Notification.new
|
99
|
-
n.app = Rpush::
|
133
|
+
n.app = Rpush::Apns::App.find_by_name("ios_app")
|
100
134
|
n.device_token = "..." # hex string
|
101
135
|
n.alert = "hi mom!"
|
102
136
|
n.data = { foo: :bar }
|
103
137
|
n.save!
|
104
138
|
```
|
139
|
+
|
140
|
+
##### Safari Push Notifications
|
141
|
+
|
142
|
+
Using one of the notifications methods above, the `url_args` attribute is available for Safari Push Notifications.
|
143
|
+
|
144
|
+
##### Environment
|
145
|
+
|
146
|
+
The app `environment` for any Apns* option is "development" for XCode installs, and "production" for app store and TestFlight. Note that for Apns2 you can now use one (production + sandbox) certificate (you don't need a separate "sandbox" or development certificate), but if you do generate a development/sandbox certificate it can only be used for "development". With Apnsp8 tokens, you can target either "development" or "production" environments.
|
147
|
+
|
105
148
|
#### Firebase Cloud Messaging
|
106
149
|
|
107
150
|
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.
|
@@ -208,7 +251,7 @@ n.save!
|
|
208
251
|
|
209
252
|
#### Windows Raw Push Notifications
|
210
253
|
|
211
|
-
Note: The data is passed as `.to_json` so only this format is supported,
|
254
|
+
Note: The data is passed as `.to_json` so only this format is supported, although raw notifications are meant to support any kind of data.
|
212
255
|
Current data structure enforces hashes and `.to_json` representation is natural presentation of it.
|
213
256
|
|
214
257
|
```ruby
|
@@ -256,6 +299,49 @@ n.save!
|
|
256
299
|
|
257
300
|
For more documentation on [Pushy](https://pushy.me/docs).
|
258
301
|
|
302
|
+
#### Webpush
|
303
|
+
|
304
|
+
[Webpush](https://tools.ietf.org/html/draft-ietf-webpush-protocol-10) is a
|
305
|
+
protocol for delivering push messages to desktop browsers. It's supported by
|
306
|
+
all major browsers (except Safari, you have to use one of the Apns transports
|
307
|
+
for that).
|
308
|
+
|
309
|
+
Using [VAPID](https://tools.ietf.org/html/draft-ietf-webpush-vapid-01), there
|
310
|
+
is no need for the sender of push notifications to register upfront with push
|
311
|
+
services (as was the case with the now legacy Mozilla or Google desktop push
|
312
|
+
providers).
|
313
|
+
|
314
|
+
Instead, you generate a pair of keys and use the public key when subscribing
|
315
|
+
users in your web app. The keys are stored along with an email address (which,
|
316
|
+
according to the spec, can be used by push service providers to contact you in
|
317
|
+
case of problems) in the `certificates` field of the Rpush Application record:
|
318
|
+
|
319
|
+
```ruby
|
320
|
+
vapid_keypair = Webpush.generate_key.to_hash
|
321
|
+
app = Rpush::Webpush::App.new
|
322
|
+
app.name = 'webpush'
|
323
|
+
app.certificate = vapid_keypair.merge(subject: 'user@example.org').to_json
|
324
|
+
app.connections = 1
|
325
|
+
app.save!
|
326
|
+
```
|
327
|
+
|
328
|
+
The `subscription` object you obtain from a subscribed browser holds an
|
329
|
+
endpoint URL and cryptographic keys. When sending a notification, simply pass
|
330
|
+
the whole subscription as sole member of the `registration_ids` collection:
|
331
|
+
|
332
|
+
```ruby
|
333
|
+
n = Rpush::Webpush::Notification.new
|
334
|
+
n.app = Rpush::App.find_by_name("webpush")
|
335
|
+
n.registration_ids = [subscription]
|
336
|
+
n.data = { message: "hi mom!" }
|
337
|
+
n.save!
|
338
|
+
```
|
339
|
+
|
340
|
+
In order to send the same message to multiple devices, create one
|
341
|
+
`Notification` per device, as passing multiple subscriptions at once as
|
342
|
+
`registration_ids` is not supported.
|
343
|
+
|
344
|
+
|
259
345
|
### Running Rpush
|
260
346
|
|
261
347
|
It is recommended to run Rpush as a separate process in most cases, though embedding and manual modes are provided for low-workload environments.
|
@@ -52,6 +52,7 @@ class RpushMigrationGenerator < Rails::Generators::Base
|
|
52
52
|
add_rpush_migration('rpush_3_3_1_updates')
|
53
53
|
add_rpush_migration('rpush_4_1_0_updates')
|
54
54
|
add_rpush_migration('rpush_4_1_1_updates')
|
55
|
+
add_rpush_migration('rpush_4_2_0_updates')
|
55
56
|
end
|
56
57
|
|
57
58
|
protected
|
@@ -26,6 +26,10 @@ Rpush.configure do |config|
|
|
26
26
|
# Define a custom logger.
|
27
27
|
# config.logger = MyLogger.new
|
28
28
|
|
29
|
+
# By default in foreground mode logs are directed both to the logger and to stdout.
|
30
|
+
# If the logger goes to stdout, you can disable foreground logging to avoid duplication.
|
31
|
+
# config.foreground_logging = false
|
32
|
+
|
29
33
|
# config.apns.feedback_receiver.enabled = true
|
30
34
|
# config.apns.feedback_receiver.frequency = 60
|
31
35
|
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Rpush420Updates < ActiveRecord::VERSION::MAJOR >= 5 ? ActiveRecord::Migration["#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"] : ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
add_column :rpush_notifications, :sound_is_json, :boolean, null: true, default: false
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.down
|
7
|
+
remove_column :rpush_notifications, :sound_is_json
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
data/lib/rpush/cli.rb
CHANGED
@@ -14,7 +14,7 @@ module Rpush
|
|
14
14
|
end
|
15
15
|
|
16
16
|
class_option :config, type: :string, aliases: '-c', default: default_config_path
|
17
|
-
class_option 'rails-env', type: :string, aliases: '-e', default: 'development'
|
17
|
+
class_option 'rails-env', type: :string, aliases: '-e', default: ENV.fetch('RAILS_ENV', 'development')
|
18
18
|
|
19
19
|
option :foreground, type: :boolean, aliases: '-f', default: false
|
20
20
|
option 'pid-file', type: :string, aliases: '-p'
|
@@ -4,10 +4,10 @@ require 'rpush/client/active_model/notification'
|
|
4
4
|
require 'rpush/client/active_model/payload_data_size_validator'
|
5
5
|
require 'rpush/client/active_model/registration_ids_count_validator'
|
6
6
|
|
7
|
-
require 'rpush/client/active_model/apns/binary_notification_validator'
|
8
7
|
require 'rpush/client/active_model/apns/device_token_format_validator'
|
9
8
|
require 'rpush/client/active_model/apns/app'
|
10
9
|
require 'rpush/client/active_model/apns/notification'
|
10
|
+
require 'rpush/client/active_model/apns/notification_payload_size_validator'
|
11
11
|
|
12
12
|
require 'rpush/client/active_model/apns2/app'
|
13
13
|
require 'rpush/client/active_model/apns2/notification'
|
@@ -32,3 +32,6 @@ require 'rpush/client/active_model/wns/notification'
|
|
32
32
|
require 'rpush/client/active_model/pushy/app'
|
33
33
|
require 'rpush/client/active_model/pushy/notification'
|
34
34
|
require 'rpush/client/active_model/pushy/time_to_live_validator'
|
35
|
+
|
36
|
+
require 'rpush/client/active_model/webpush/app'
|
37
|
+
require 'rpush/client/active_model/webpush/notification'
|
@@ -5,7 +5,7 @@ module Rpush
|
|
5
5
|
class DataValidator < ::ActiveModel::Validator
|
6
6
|
def validate(record)
|
7
7
|
return unless record.collapse_key.nil? && record.data.nil?
|
8
|
-
record.errors
|
8
|
+
record.errors.add :data, 'must be set unless collapse_key is specified'
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -4,8 +4,8 @@ module Rpush
|
|
4
4
|
module Apns
|
5
5
|
class DeviceTokenFormatValidator < ::ActiveModel::Validator
|
6
6
|
def validate(record)
|
7
|
-
return if record.device_token =~
|
8
|
-
record.errors
|
7
|
+
return if record.device_token =~ /\A[a-z0-9]\w+\z/i
|
8
|
+
record.errors.add :device_token, "is invalid"
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -7,15 +7,23 @@ module Rpush
|
|
7
7
|
APNS_PRIORITY_IMMEDIATE = 10
|
8
8
|
APNS_PRIORITY_CONSERVE_POWER = 5
|
9
9
|
APNS_PRIORITIES = [APNS_PRIORITY_IMMEDIATE, APNS_PRIORITY_CONSERVE_POWER]
|
10
|
+
MAX_PAYLOAD_BYTESIZE = 2048
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def max_payload_bytesize
|
14
|
+
MAX_PAYLOAD_BYTESIZE
|
15
|
+
end
|
16
|
+
end
|
10
17
|
|
11
18
|
def self.included(base)
|
19
|
+
base.extend ClassMethods
|
12
20
|
base.instance_eval do
|
13
21
|
validates :device_token, presence: true
|
14
22
|
validates :badge, numericality: true, allow_nil: true
|
15
23
|
validates :priority, inclusion: { in: APNS_PRIORITIES }, allow_nil: true
|
16
24
|
|
17
25
|
validates_with Rpush::Client::ActiveModel::Apns::DeviceTokenFormatValidator
|
18
|
-
validates_with Rpush::Client::ActiveModel::Apns::
|
26
|
+
validates_with Rpush::Client::ActiveModel::Apns::NotificationPayloadSizeValidator
|
19
27
|
|
20
28
|
base.const_set('APNS_DEFAULT_EXPIRY', APNS_DEFAULT_EXPIRY) unless base.const_defined?('APNS_DEFAULT_EXPIRY')
|
21
29
|
base.const_set('APNS_PRIORITY_IMMEDIATE', APNS_PRIORITY_IMMEDIATE) unless base.const_defined?('APNS_PRIORITY_IMMEDIATE')
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Client
|
3
|
+
module ActiveModel
|
4
|
+
module Apns
|
5
|
+
class NotificationPayloadSizeValidator < ::ActiveModel::Validator
|
6
|
+
def validate(record)
|
7
|
+
limit = record.class.max_payload_bytesize
|
8
|
+
return unless record.payload.bytesize > limit
|
9
|
+
record.errors.add :base, "APN notification cannot be larger than #{limit} bytes. Try condensing your alert and device attributes."
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -3,6 +3,20 @@ module Rpush
|
|
3
3
|
module ActiveModel
|
4
4
|
module Apns2
|
5
5
|
include Rpush::Client::ActiveModel::Apns
|
6
|
+
|
7
|
+
module Notification
|
8
|
+
MAX_PAYLOAD_BYTESIZE = 4096
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def max_payload_bytesize
|
12
|
+
MAX_PAYLOAD_BYTESIZE
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.included(base)
|
17
|
+
base.extend ClassMethods
|
18
|
+
end
|
19
|
+
end
|
6
20
|
end
|
7
21
|
end
|
8
22
|
end
|
@@ -5,7 +5,7 @@ module Rpush
|
|
5
5
|
class ExpiryCollapseKeyMutualInclusionValidator < ::ActiveModel::Validator
|
6
6
|
def validate(record)
|
7
7
|
return unless record.collapse_key && !record.expiry
|
8
|
-
record.errors
|
8
|
+
record.errors.add :expiry, 'must be set when using a collapse_key'
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -26,16 +26,16 @@ module Rpush
|
|
26
26
|
# I'm not happy about it, but this will have to do until I can take a further look.
|
27
27
|
def priority=(priority)
|
28
28
|
case priority
|
29
|
-
when 'high'
|
29
|
+
when 'high', GCM_PRIORITY_HIGH
|
30
30
|
super(GCM_PRIORITY_HIGH)
|
31
|
-
when 'normal'
|
31
|
+
when 'normal', GCM_PRIORITY_NORMAL
|
32
32
|
super(GCM_PRIORITY_NORMAL)
|
33
33
|
else
|
34
34
|
errors.add(:priority, 'must be one of either "normal" or "high"')
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
def as_json(options = nil)
|
38
|
+
def as_json(options = nil) # rubocop:disable Metrics/PerceivedComplexity
|
39
39
|
json = {
|
40
40
|
'registration_ids' => registration_ids,
|
41
41
|
'delay_while_idle' => delay_while_idle,
|
@@ -43,6 +43,7 @@ module Rpush
|
|
43
43
|
}
|
44
44
|
json['collapse_key'] = collapse_key if collapse_key
|
45
45
|
json['content_available'] = content_available if content_available
|
46
|
+
json['mutable_content'] = mutable_content if mutable_content
|
46
47
|
json['dry_run'] = dry_run if dry_run
|
47
48
|
json['notification'] = notification if notification
|
48
49
|
json['priority'] = priority_for_notification if priority
|
@@ -5,7 +5,7 @@ module Rpush
|
|
5
5
|
def validate(record)
|
6
6
|
limit = options[:limit] || 1024
|
7
7
|
return unless record.data && record.payload_data_size > limit
|
8
|
-
record.errors
|
8
|
+
record.errors.add :base, "Notification payload data cannot be larger than #{limit} bytes."
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -5,7 +5,7 @@ module Rpush
|
|
5
5
|
def validate(record)
|
6
6
|
limit = options[:limit] || 100
|
7
7
|
return unless record.registration_ids && record.registration_ids.size > limit
|
8
|
-
record.errors
|
8
|
+
record.errors.add :base, "Number of registration_ids cannot be larger than #{limit}."
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Client
|
3
|
+
module ActiveModel
|
4
|
+
module Webpush
|
5
|
+
module App
|
6
|
+
|
7
|
+
class VapidKeypairValidator < ::ActiveModel::Validator
|
8
|
+
def validate(record)
|
9
|
+
return if record.vapid_keypair.blank?
|
10
|
+
keypair = record.vapid
|
11
|
+
%i[ subject public_key private_key ].each do |key|
|
12
|
+
unless keypair.key?(key)
|
13
|
+
record.errors.add(:vapid_keypair, "must have a #{key} entry")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
rescue
|
17
|
+
record.errors.add(:vapid_keypair, 'must be valid JSON')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.included(base)
|
22
|
+
base.class_eval do
|
23
|
+
alias_attribute :vapid_keypair, :certificate
|
24
|
+
validates :vapid_keypair, presence: true
|
25
|
+
validates_with VapidKeypairValidator
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def service_name
|
30
|
+
'webpush'
|
31
|
+
end
|
32
|
+
|
33
|
+
def vapid
|
34
|
+
@vapid ||= JSON.parse(vapid_keypair).symbolize_keys
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Client
|
3
|
+
module ActiveModel
|
4
|
+
module Webpush
|
5
|
+
module Notification
|
6
|
+
|
7
|
+
class RegistrationValidator < ::ActiveModel::Validator
|
8
|
+
KEYS = %i[ endpoint keys ].freeze
|
9
|
+
def validate(record)
|
10
|
+
return if record.registration_ids.blank?
|
11
|
+
return if record.registration_ids.size > 1
|
12
|
+
reg = record.registration_ids.first
|
13
|
+
unless reg.is_a?(Hash) &&
|
14
|
+
reg.keys.sort == KEYS &&
|
15
|
+
reg[:endpoint].is_a?(String) &&
|
16
|
+
reg[:keys].is_a?(Hash)
|
17
|
+
record.errors.add(:base, 'Registration must have :endpoint (String) and :keys (Hash) keys')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(base)
|
23
|
+
base.instance_eval do
|
24
|
+
alias_attribute :time_to_live, :expiry
|
25
|
+
|
26
|
+
validates :registration_ids, presence: true
|
27
|
+
validates :data, presence: true
|
28
|
+
validates :time_to_live, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true
|
29
|
+
|
30
|
+
validates_with Rpush::Client::ActiveModel::PayloadDataSizeValidator, limit: 4096
|
31
|
+
validates_with Rpush::Client::ActiveModel::RegistrationIdsCountValidator, limit: 1
|
32
|
+
validates_with RegistrationValidator
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def data=(value)
|
37
|
+
value = value.stringify_keys if value.respond_to?(:stringify_keys)
|
38
|
+
super value
|
39
|
+
end
|
40
|
+
|
41
|
+
def subscription
|
42
|
+
@subscription ||= registration_ids.first.deep_symbolize_keys
|
43
|
+
end
|
44
|
+
|
45
|
+
def message
|
46
|
+
data['message'].presence if data
|
47
|
+
end
|
48
|
+
|
49
|
+
# https://webpush-wg.github.io/webpush-protocol/#urgency
|
50
|
+
def urgency
|
51
|
+
data['urgency'].presence if data
|
52
|
+
end
|
53
|
+
|
54
|
+
def as_json(_options = nil)
|
55
|
+
{
|
56
|
+
'data' => data,
|
57
|
+
'time_to_live' => time_to_live,
|
58
|
+
'registration_ids' => registration_ids
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|