rpush 4.1.1 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +261 -161
  3. data/README.md +103 -17
  4. data/lib/generators/rpush_migration_generator.rb +1 -0
  5. data/lib/generators/templates/rpush.rb +4 -0
  6. data/lib/generators/templates/rpush_4_2_0_updates.rb +10 -0
  7. data/lib/rpush/cli.rb +1 -1
  8. data/lib/rpush/client/active_model.rb +4 -1
  9. data/lib/rpush/client/active_model/adm/data_validator.rb +1 -1
  10. data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +2 -2
  11. data/lib/rpush/client/active_model/apns/notification.rb +9 -1
  12. data/lib/rpush/client/active_model/apns/notification_payload_size_validator.rb +15 -0
  13. data/lib/rpush/client/active_model/apns2/notification.rb +14 -0
  14. data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +1 -1
  15. data/lib/rpush/client/active_model/gcm/notification.rb +4 -3
  16. data/lib/rpush/client/active_model/payload_data_size_validator.rb +1 -1
  17. data/lib/rpush/client/active_model/registration_ids_count_validator.rb +1 -1
  18. data/lib/rpush/client/active_model/webpush/app.rb +41 -0
  19. data/lib/rpush/client/active_model/webpush/notification.rb +66 -0
  20. data/lib/rpush/client/active_model/wns/notification.rb +8 -0
  21. data/lib/rpush/client/active_record.rb +4 -0
  22. data/lib/rpush/client/active_record/apns/active_record_serializable_notification.rb +65 -0
  23. data/lib/rpush/client/active_record/apns/notification.rb +1 -29
  24. data/lib/rpush/client/active_record/apns2/notification.rb +4 -1
  25. data/lib/rpush/client/active_record/apnsp8/notification.rb +1 -0
  26. data/lib/rpush/client/active_record/app.rb +1 -1
  27. data/lib/rpush/client/active_record/notification.rb +1 -1
  28. data/lib/rpush/client/active_record/webpush/app.rb +11 -0
  29. data/lib/rpush/client/active_record/webpush/notification.rb +12 -0
  30. data/lib/rpush/client/redis.rb +3 -0
  31. data/lib/rpush/client/redis/apns2/notification.rb +1 -0
  32. data/lib/rpush/client/redis/apnsp8/notification.rb +2 -0
  33. data/lib/rpush/client/redis/notification.rb +2 -1
  34. data/lib/rpush/client/redis/pushy/notification.rb +0 -1
  35. data/lib/rpush/client/redis/webpush/app.rb +15 -0
  36. data/lib/rpush/client/redis/webpush/notification.rb +15 -0
  37. data/lib/rpush/configuration.rb +3 -2
  38. data/lib/rpush/daemon.rb +4 -1
  39. data/lib/rpush/daemon/apns/feedback_receiver.rb +2 -1
  40. data/lib/rpush/daemon/apns2/delivery.rb +13 -2
  41. data/lib/rpush/daemon/apnsp8/delivery.rb +7 -2
  42. data/lib/rpush/daemon/app_runner.rb +1 -1
  43. data/lib/rpush/daemon/batch.rb +12 -5
  44. data/lib/rpush/daemon/delivery.rb +1 -2
  45. data/lib/rpush/daemon/dispatcher/apns_http2.rb +4 -2
  46. data/lib/rpush/daemon/dispatcher/apnsp8_http2.rb +2 -1
  47. data/lib/rpush/daemon/signal_handler.rb +1 -1
  48. data/lib/rpush/daemon/store/active_record.rb +1 -1
  49. data/lib/rpush/daemon/store/active_record/reconnectable.rb +1 -1
  50. data/lib/rpush/daemon/store/redis.rb +1 -1
  51. data/lib/rpush/daemon/webpush.rb +10 -0
  52. data/lib/rpush/daemon/webpush/delivery.rb +114 -0
  53. data/lib/rpush/daemon/wns/badge_request.rb +8 -3
  54. data/lib/rpush/daemon/wns/raw_request.rb +9 -2
  55. data/lib/rpush/daemon/wns/toast_request.rb +6 -2
  56. data/lib/rpush/logger.rb +1 -0
  57. data/lib/rpush/version.rb +3 -3
  58. data/spec/.rubocop.yml +1 -1
  59. data/spec/functional/apns2_spec.rb +97 -2
  60. data/spec/functional/gcm_priority_spec.rb +40 -0
  61. data/spec/functional/webpush_spec.rb +30 -0
  62. data/spec/spec_helper.rb +2 -0
  63. data/spec/support/active_record_setup.rb +3 -1
  64. data/spec/support/simplecov_helper.rb +1 -1
  65. data/spec/unit/client/active_record/adm/app_spec.rb +2 -54
  66. data/spec/unit/client/active_record/adm/notification_spec.rb +2 -39
  67. data/spec/unit/client/active_record/apns/app_spec.rb +3 -26
  68. data/spec/unit/client/active_record/apns/feedback_spec.rb +1 -5
  69. data/spec/unit/client/active_record/apns/notification_spec.rb +29 -288
  70. data/spec/unit/client/active_record/apns2/app_spec.rb +4 -0
  71. data/spec/unit/client/active_record/apns2/notification_spec.rb +65 -0
  72. data/spec/unit/client/active_record/apnsp8/notification_spec.rb +28 -0
  73. data/spec/unit/client/active_record/app_spec.rb +1 -26
  74. data/spec/unit/client/active_record/gcm/app_spec.rb +3 -1
  75. data/spec/unit/client/active_record/gcm/notification_spec.rb +6 -83
  76. data/spec/unit/client/active_record/notification_spec.rb +10 -11
  77. data/spec/unit/client/active_record/pushy/app_spec.rb +2 -13
  78. data/spec/unit/client/active_record/pushy/notification_spec.rb +2 -55
  79. data/spec/unit/client/active_record/shared/app.rb +14 -0
  80. data/spec/unit/{notification_shared.rb → client/active_record/shared/notification.rb} +12 -7
  81. data/spec/unit/client/active_record/webpush/app_spec.rb +6 -0
  82. data/spec/unit/client/active_record/webpush/notification_spec.rb +6 -0
  83. data/spec/unit/client/active_record/wns/badge_notification_spec.rb +1 -11
  84. data/spec/unit/client/active_record/wns/raw_notification_spec.rb +3 -12
  85. data/spec/unit/client/active_record/wpns/app_spec.rb +3 -1
  86. data/spec/unit/client/active_record/wpns/notification_spec.rb +2 -17
  87. data/spec/unit/client/redis/adm/app_spec.rb +5 -0
  88. data/spec/unit/client/redis/adm/notification_spec.rb +5 -0
  89. data/spec/unit/client/redis/apns/app_spec.rb +5 -0
  90. data/spec/unit/client/redis/apns/feedback_spec.rb +5 -0
  91. data/spec/unit/client/redis/apns/notification_spec.rb +50 -0
  92. data/spec/unit/client/redis/apns2/app_spec.rb +4 -0
  93. data/spec/unit/client/redis/apns2/notification_spec.rb +50 -0
  94. data/spec/unit/client/redis/apnsp8/notification_spec.rb +29 -0
  95. data/spec/unit/client/redis/app_spec.rb +5 -0
  96. data/spec/unit/client/redis/gcm/app_spec.rb +5 -0
  97. data/spec/unit/client/redis/gcm/notification_spec.rb +5 -0
  98. data/spec/unit/client/redis/notification_spec.rb +5 -0
  99. data/spec/unit/client/redis/pushy/app_spec.rb +5 -0
  100. data/spec/unit/client/redis/pushy/notification_spec.rb +5 -0
  101. data/spec/unit/client/redis/webpush/app_spec.rb +5 -0
  102. data/spec/unit/client/redis/webpush/notification_spec.rb +5 -0
  103. data/spec/unit/client/redis/wns/badge_notification_spec.rb +5 -0
  104. data/spec/unit/client/redis/wns/raw_notification_spec.rb +22 -0
  105. data/spec/unit/client/redis/wpns/app_spec.rb +5 -0
  106. data/spec/unit/client/redis/wpns/notification_spec.rb +5 -0
  107. data/spec/unit/client/shared/adm/app.rb +51 -0
  108. data/spec/unit/client/shared/adm/notification.rb +39 -0
  109. data/spec/unit/client/shared/apns/app.rb +29 -0
  110. data/spec/unit/client/shared/apns/feedback.rb +9 -0
  111. data/spec/unit/client/shared/apns/notification.rb +262 -0
  112. data/spec/unit/client/shared/app.rb +17 -0
  113. data/spec/unit/client/shared/gcm/app.rb +4 -0
  114. data/spec/unit/client/shared/gcm/notification.rb +77 -0
  115. data/spec/unit/client/shared/notification.rb +10 -0
  116. data/spec/unit/client/shared/pushy/app.rb +17 -0
  117. data/spec/unit/client/shared/pushy/notification.rb +55 -0
  118. data/spec/unit/client/shared/webpush/app.rb +33 -0
  119. data/spec/unit/client/shared/webpush/notification.rb +83 -0
  120. data/spec/unit/client/shared/wns/badge_notification.rb +15 -0
  121. data/spec/unit/client/shared/wns/raw_notification.rb +21 -0
  122. data/spec/unit/client/shared/wpns/app.rb +4 -0
  123. data/spec/unit/client/shared/wpns/notification.rb +18 -0
  124. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +19 -1
  125. data/spec/unit/daemon/batch_spec.rb +50 -2
  126. data/spec/unit/daemon/delivery_spec.rb +10 -0
  127. data/spec/unit/daemon/gcm/delivery_spec.rb +1 -1
  128. data/spec/unit/daemon/shared/store.rb +312 -0
  129. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +7 -7
  130. data/spec/unit/daemon/store/active_record_spec.rb +7 -295
  131. data/spec/unit/daemon/store/redis_spec.rb +4 -293
  132. data/spec/unit/daemon/webpush/delivery_spec.rb +142 -0
  133. data/spec/unit/daemon/wns/post_request_spec.rb +64 -0
  134. data/spec/unit_spec_helper.rb +3 -0
  135. metadata +140 -13
  136. 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. **Note:** Redis support for Rails 5.2 is not yet working if you're using Modis, see [this issue](https://github.com/ileitch/modis/issues/13).
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
- If this is your first time using the APNs, you will need to generate SSL certificates. See [Generating Certificates](https://github.com/rpush/rpush/wiki/Generating-Certificates) for instructions.
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::Apns::App.new
70
+ app = Rpush::Apnsp8::App.new
60
71
  app.name = "ios_app"
61
- app.certificate = File.read("/path/to/sandbox.pem")
72
+ app.apn_key = File.read("/path/to/sandbox.p8")
62
73
  app.environment = "development" # APNs environment.
63
- app.password = "certificate password"
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::Apns::App.find_by_name("ios_app")
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
- The `url_args` attribute is available for Safari Push Notifications.
90
+ ##### Apns2
78
91
 
79
- 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.
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
- To use the newer APNs Api replace `Rpush::Apns::App` with `Rpush::Apns2::App`.
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
- To use the p8 APNs Api replace `Rpush::Apns::App` with `Rpush::Apnsp8::App`.
119
+ ##### Apns (legacy protocol)
84
120
 
85
121
  ```ruby
86
- app = Rpush::Apnsp8::App.new
122
+ app = Rpush::Apns::App.new
87
123
  app.name = "ios_app"
88
- app.apn_key = File.read("/path/to/sandbox.p8")
124
+ app.certificate = File.read("/path/to/sandbox.pem")
89
125
  app.environment = "development" # APNs environment.
90
- app.apn_key_id = "APN KEY ID"
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::Apnsp8::App.find_by_name("ios_app")
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, altough raw notifications are meant to support any kind of data.
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
+
@@ -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[:data] << 'must be set unless collapse_key is specified'
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 =~ /^[a-z0-9]\w+$/i
8
- record.errors[:device_token] << "is invalid"
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::BinaryNotificationValidator
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[:expiry] << 'must be set when using a collapse_key'
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[:base] << "Notification payload data cannot be larger than #{limit} bytes."
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[:base] << "Number of registration_ids cannot be larger than #{limit}."
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