rpush 5.2.0 → 5.3.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 +16 -0
- data/README.md +49 -4
- data/lib/rpush/client/active_model.rb +3 -0
- 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_payload_size_validator.rb +1 -1
- data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +1 -1
- 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_record.rb +3 -0
- data/lib/rpush/client/active_record/apnsp8/notification.rb +1 -0
- 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/apnsp8/notification.rb +2 -0
- 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 +1 -1
- data/lib/rpush/daemon.rb +3 -0
- data/lib/rpush/daemon/apns2/delivery.rb +7 -1
- data/lib/rpush/daemon/webpush.rb +10 -0
- data/lib/rpush/daemon/webpush/delivery.rb +114 -0
- data/lib/rpush/version.rb +1 -1
- data/spec/functional/apns2_spec.rb +12 -2
- data/spec/functional/webpush_spec.rb +30 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/client/active_record/apnsp8/notification_spec.rb +28 -0
- 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/redis/apnsp8/notification_spec.rb +29 -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/shared/webpush/app.rb +33 -0
- data/spec/unit/client/shared/webpush/notification.rb +83 -0
- data/spec/unit/daemon/webpush/delivery_spec.rb +142 -0
- metadata +44 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2da7543fed0193b0dcd20169efb5abd8170f15bc5043ef4ce2cb4d0e56380b4f
|
|
4
|
+
data.tar.gz: d7ba0f5b1dc97d17a7d23bf623d900b6da26b428ddcf4a8a93fce7641ab20f2d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 745e0799a2e66d4813b99c4a93eb5d172dcbbe4c1317bd945b1addf177720f489bbdb0d83d918b9ab0ba4b98c35d186c0522d39740541097e16e90d0687487d8
|
|
7
|
+
data.tar.gz: e6d249ad9c7366cc54267e4eaa6f3d1bb3670dc6211380aa8bced26b33053cc0fda328f33b72a1d843cdd794e44e0793cd579e49745c60e46aa83155a5d0acca
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v5.3.0](https://github.com/rpush/rpush/tree/v5.3.0) (2021-01-07)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/rpush/rpush/compare/v5.2.0...v5.3.0)
|
|
6
|
+
|
|
7
|
+
**Implemented enhancements:**
|
|
8
|
+
|
|
9
|
+
- support for Webpush with VAPID [\#574](https://github.com/rpush/rpush/pull/574) ([jkraemer](https://github.com/jkraemer))
|
|
10
|
+
|
|
11
|
+
**Merged pull requests:**
|
|
12
|
+
|
|
13
|
+
- Bug fix: APNS P8 Notifications Are Marked as Invalid When the Payload Exceeds 2kb \(2048 bytes\) [\#583](https://github.com/rpush/rpush/pull/583) ([gregblake](https://github.com/gregblake))
|
|
14
|
+
- Fix more Rails 6.1 deprecation warnings [\#582](https://github.com/rpush/rpush/pull/582) ([jas14](https://github.com/jas14))
|
|
15
|
+
- Feature/apns2 default headers [\#579](https://github.com/rpush/rpush/pull/579) ([jkraemer](https://github.com/jkraemer))
|
|
16
|
+
- Fix APNS2 documentation in README [\#578](https://github.com/rpush/rpush/pull/578) ([jamestjw](https://github.com/jamestjw))
|
|
17
|
+
- Fixed typo with misspell [\#575](https://github.com/rpush/rpush/pull/575) ([hsbt](https://github.com/hsbt))
|
|
18
|
+
|
|
3
19
|
## [v5.2.0](https://github.com/rpush/rpush/tree/v5.2.0) (2020-10-08)
|
|
4
20
|
|
|
5
21
|
[Full Changelog](https://github.com/rpush/rpush/compare/v5.1.0...v5.2.0)
|
data/README.md
CHANGED
|
@@ -18,6 +18,7 @@ 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
|
|
|
@@ -96,6 +97,7 @@ app.name = "ios_app"
|
|
|
96
97
|
app.certificate = File.read("/path/to/sandbox.pem")
|
|
97
98
|
app.environment = "development"
|
|
98
99
|
app.password = "certificate password"
|
|
100
|
+
app.bundle_id = "BUNDLE ID" # the unique bundle id of the app, like com.example.appname
|
|
99
101
|
app.connections = 1
|
|
100
102
|
app.save!
|
|
101
103
|
```
|
|
@@ -106,9 +108,9 @@ n.app = Rpush::Apns2::App.find_by_name("ios_app")
|
|
|
106
108
|
n.device_token = "..." # hex string
|
|
107
109
|
n.alert = "hi mom!"
|
|
108
110
|
n.data = {
|
|
109
|
-
headers: { 'apns-topic': "BUNDLE ID", # the bundle id of the app, like com.example.appname
|
|
110
|
-
foo: :bar
|
|
111
|
-
|
|
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
|
+
}
|
|
112
114
|
n.save!
|
|
113
115
|
```
|
|
114
116
|
|
|
@@ -249,7 +251,7 @@ n.save!
|
|
|
249
251
|
|
|
250
252
|
#### Windows Raw Push Notifications
|
|
251
253
|
|
|
252
|
-
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.
|
|
253
255
|
Current data structure enforces hashes and `.to_json` representation is natural presentation of it.
|
|
254
256
|
|
|
255
257
|
```ruby
|
|
@@ -297,6 +299,49 @@ n.save!
|
|
|
297
299
|
|
|
298
300
|
For more documentation on [Pushy](https://pushy.me/docs).
|
|
299
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
|
+
|
|
300
345
|
### Running Rpush
|
|
301
346
|
|
|
302
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.
|
|
@@ -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
|
|
@@ -6,7 +6,7 @@ module Rpush
|
|
|
6
6
|
def validate(record)
|
|
7
7
|
limit = record.class.max_payload_bytesize
|
|
8
8
|
return unless record.payload.bytesize > limit
|
|
9
|
-
record.errors
|
|
9
|
+
record.errors.add :base, "APN notification cannot be larger than #{limit} bytes. Try condensing your alert and device attributes."
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
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
|
|
@@ -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
|
|
@@ -32,3 +32,6 @@ require 'rpush/client/active_record/adm/app'
|
|
|
32
32
|
|
|
33
33
|
require 'rpush/client/active_record/pushy/notification'
|
|
34
34
|
require 'rpush/client/active_record/pushy/app'
|
|
35
|
+
|
|
36
|
+
require 'rpush/client/active_record/webpush/notification'
|
|
37
|
+
require 'rpush/client/active_record/webpush/app'
|
data/lib/rpush/client/redis.rb
CHANGED
|
@@ -44,6 +44,9 @@ require 'rpush/client/redis/wns/badge_notification'
|
|
|
44
44
|
require 'rpush/client/redis/pushy/app'
|
|
45
45
|
require 'rpush/client/redis/pushy/notification'
|
|
46
46
|
|
|
47
|
+
require 'rpush/client/redis/webpush/app'
|
|
48
|
+
require 'rpush/client/redis/webpush/notification'
|
|
49
|
+
|
|
47
50
|
Modis.configure do |config|
|
|
48
51
|
config.namespace = :rpush
|
|
49
52
|
end
|
|
@@ -3,6 +3,8 @@ module Rpush
|
|
|
3
3
|
module Redis
|
|
4
4
|
module Apnsp8
|
|
5
5
|
class Notification < Rpush::Client::Redis::Notification
|
|
6
|
+
include Rpush::Client::ActiveModel::Apns::Notification
|
|
7
|
+
include Rpush::Client::ActiveModel::Apns2::Notification
|
|
6
8
|
include Rpush::Client::ActiveModel::Apnsp8::Notification
|
|
7
9
|
end
|
|
8
10
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Client
|
|
3
|
+
module Redis
|
|
4
|
+
module Webpush
|
|
5
|
+
class Notification < Rpush::Client::Redis::Notification
|
|
6
|
+
include Rpush::Client::ActiveModel::Webpush::Notification
|
|
7
|
+
|
|
8
|
+
def time_to_live=(value)
|
|
9
|
+
self.expiry = value
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
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].each do |service|
|
|
109
|
+
[:Apns, :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
|
|
data/lib/rpush/daemon.rb
CHANGED
|
@@ -107,7 +107,13 @@ module Rpush
|
|
|
107
107
|
end
|
|
108
108
|
|
|
109
109
|
def prepare_headers(notification)
|
|
110
|
-
|
|
110
|
+
headers = {}
|
|
111
|
+
|
|
112
|
+
headers['apns-expiration'] = '0'
|
|
113
|
+
headers['apns-priority'] = '10'
|
|
114
|
+
headers['apns-topic'] = @app.bundle_id
|
|
115
|
+
|
|
116
|
+
headers.merge notification_data(notification)[HTTP2_HEADERS_KEY] || {}
|
|
111
117
|
end
|
|
112
118
|
|
|
113
119
|
def notification_data(notification)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webpush"
|
|
4
|
+
|
|
5
|
+
module Rpush
|
|
6
|
+
module Daemon
|
|
7
|
+
module Webpush
|
|
8
|
+
|
|
9
|
+
# Webpush::Request handles all the encryption / signing.
|
|
10
|
+
# We just override #perform to inject the http instance that is managed
|
|
11
|
+
# by Rpush.
|
|
12
|
+
#
|
|
13
|
+
class Request < ::Webpush::Request
|
|
14
|
+
def perform(http)
|
|
15
|
+
req = Net::HTTP::Post.new(uri.request_uri, headers)
|
|
16
|
+
req.body = body
|
|
17
|
+
http.request(uri, req)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Delivery < Rpush::Daemon::Delivery
|
|
22
|
+
|
|
23
|
+
OK = [ 200, 201, 202 ].freeze
|
|
24
|
+
TEMPORARY_FAILURES = [ 429, 500, 502, 503, 504 ].freeze
|
|
25
|
+
|
|
26
|
+
def initialize(app, http, notification, batch)
|
|
27
|
+
@app = app
|
|
28
|
+
@http = http
|
|
29
|
+
@notification = notification
|
|
30
|
+
@batch = batch
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def perform
|
|
34
|
+
response = send_request
|
|
35
|
+
process_response response
|
|
36
|
+
rescue SocketError, SystemCallError => error
|
|
37
|
+
mark_retryable(@notification, Time.now + 10.seconds, error)
|
|
38
|
+
raise
|
|
39
|
+
rescue StandardError => error
|
|
40
|
+
mark_failed(error)
|
|
41
|
+
raise
|
|
42
|
+
ensure
|
|
43
|
+
@batch.notification_processed
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def send_request
|
|
49
|
+
# The initializer is inherited from Webpush::Request and looks like
|
|
50
|
+
# this:
|
|
51
|
+
#
|
|
52
|
+
# initialize(message: '', subscription:, vapid:, **options)
|
|
53
|
+
#
|
|
54
|
+
# where subscription is a hash of :endpoint and :keys, and vapid
|
|
55
|
+
# holds the vapid public and private keys and the :subject (which is
|
|
56
|
+
# an email address).
|
|
57
|
+
Request.new(
|
|
58
|
+
message: @notification.message,
|
|
59
|
+
subscription: @notification.subscription,
|
|
60
|
+
vapid: @app.vapid,
|
|
61
|
+
ttl: @notification.time_to_live,
|
|
62
|
+
urgency: @notification.urgency
|
|
63
|
+
).perform(@http)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def process_response(response)
|
|
67
|
+
case response.code.to_i
|
|
68
|
+
when *OK
|
|
69
|
+
mark_delivered
|
|
70
|
+
when *TEMPORARY_FAILURES
|
|
71
|
+
retry_delivery(response)
|
|
72
|
+
else
|
|
73
|
+
fail_delivery(response)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def retry_delivery(response)
|
|
78
|
+
time = deliver_after_header(response)
|
|
79
|
+
if time
|
|
80
|
+
mark_retryable(@notification, time)
|
|
81
|
+
else
|
|
82
|
+
mark_retryable_exponential(@notification)
|
|
83
|
+
end
|
|
84
|
+
log_info("Webpush endpoint responded with a #{response.code} error. #{retry_message}")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def fail_delivery(response)
|
|
88
|
+
fail_message = fail_message(response)
|
|
89
|
+
log_error("#{@notification.id} failed: #{fail_message}")
|
|
90
|
+
fail Rpush::DeliveryError.new(response.code.to_i, @notification.id, fail_message)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def deliver_after_header(response)
|
|
94
|
+
Rpush::Daemon::RetryHeaderParser.parse(response.header['retry-after'])
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def retry_message
|
|
98
|
+
deliver_after = @notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')
|
|
99
|
+
"Notification #{@notification.id} will be retried after #{deliver_after} (retry #{@notification.retries})."
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def fail_message(response)
|
|
103
|
+
msg = Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i]
|
|
104
|
+
if explanation = response.body.to_s[0..200].presence
|
|
105
|
+
msg += ": #{explanation}"
|
|
106
|
+
end
|
|
107
|
+
msg
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
data/lib/rpush/version.rb
CHANGED
|
@@ -42,6 +42,7 @@ describe 'APNs http2 adapter' do
|
|
|
42
42
|
app.certificate = TEST_CERT
|
|
43
43
|
app.name = 'test'
|
|
44
44
|
app.environment = 'development'
|
|
45
|
+
app.bundle_id = 'com.example.app'
|
|
45
46
|
app.save!
|
|
46
47
|
app
|
|
47
48
|
end
|
|
@@ -75,7 +76,12 @@ describe 'APNs http2 adapter' do
|
|
|
75
76
|
:post,
|
|
76
77
|
"/3/device/#{fake_device_token}",
|
|
77
78
|
{ body: "{\"aps\":{\"alert\":\"test\",\"sound\":\"default\",\"content-available\":1}}",
|
|
78
|
-
headers: {
|
|
79
|
+
headers: {
|
|
80
|
+
'apns-expiration' => '0',
|
|
81
|
+
'apns-priority' => '10',
|
|
82
|
+
'apns-topic' => 'com.example.app'
|
|
83
|
+
}
|
|
84
|
+
}
|
|
79
85
|
)
|
|
80
86
|
.and_return(fake_http2_request)
|
|
81
87
|
|
|
@@ -104,7 +110,11 @@ describe 'APNs http2 adapter' do
|
|
|
104
110
|
"/3/device/#{fake_device_token}",
|
|
105
111
|
{ body: "{\"aps\":{\"alert\":\"test\",\"sound\":\"default\","\
|
|
106
112
|
"\"content-available\":1},\"some_field\":\"some value\"}",
|
|
107
|
-
headers: {
|
|
113
|
+
headers: {
|
|
114
|
+
'apns-topic' => bundle_id,
|
|
115
|
+
'apns-expiration' => '0',
|
|
116
|
+
'apns-priority' => '10'
|
|
117
|
+
}
|
|
108
118
|
}
|
|
109
119
|
).and_return(fake_http2_request)
|
|
110
120
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'functional_spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Webpush' do
|
|
4
|
+
let(:code) { 201 }
|
|
5
|
+
let(:response) { instance_double('Net::HTTPResponse', code: code, body: '') }
|
|
6
|
+
let(:http) { instance_double('Net::HTTP::Persistent', request: response, shutdown: nil) }
|
|
7
|
+
let(:app) { Rpush::Webpush::App.create!(name: 'MyApp', vapid_keypair: VAPID_KEYPAIR) }
|
|
8
|
+
|
|
9
|
+
let(:device_reg) {
|
|
10
|
+
{ endpoint: 'https://webpush-provider.example.org/push/some-id',
|
|
11
|
+
keys: {'auth' => 'DgN9EBia1o057BdhCOGURA', 'p256dh' => 'BAtxJ--7vHq9IVm8utUB3peJ4lpxRqk1rukCIkVJOomS83QkCnrQ4EyYQsSaCRgy_c8XPytgXxuyAvRJdnTPK4A'} }
|
|
12
|
+
}
|
|
13
|
+
let(:notification) { Rpush::Webpush::Notification.create!(app: app, registration_ids: [device_reg], data: { message: 'test' }) }
|
|
14
|
+
|
|
15
|
+
before do
|
|
16
|
+
allow(Net::HTTP::Persistent).to receive_messages(new: http)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'deliveres a notification successfully' do
|
|
20
|
+
expect { Rpush.push }.to change { notification.reload.delivered }.to(true)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context 'when delivery failed' do
|
|
24
|
+
let(:code) { 404 }
|
|
25
|
+
it 'marks a notification as failed' do
|
|
26
|
+
expect { Rpush.push }.to change { notification.reload.failed }.to(true)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
data/spec/spec_helper.rb
CHANGED
|
@@ -46,6 +46,8 @@ path = File.join(File.dirname(__FILE__), 'support')
|
|
|
46
46
|
TEST_CERT = File.read(File.join(path, 'cert_without_password.pem'))
|
|
47
47
|
TEST_CERT_WITH_PASSWORD = File.read(File.join(path, 'cert_with_password.pem'))
|
|
48
48
|
|
|
49
|
+
VAPID_KEYPAIR = Webpush.generate_key.to_hash.merge(subject: 'rpush-test@example.org').to_json
|
|
50
|
+
|
|
49
51
|
def after_example_cleanup
|
|
50
52
|
Rpush.logger = nil
|
|
51
53
|
Rpush::Daemon.store = nil
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "unit_spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Rpush::Client::ActiveRecord::Apnsp8::Notification do
|
|
4
|
+
subject(:notification) { described_class.new }
|
|
5
|
+
|
|
6
|
+
it_behaves_like 'Rpush::Client::Apns::Notification'
|
|
7
|
+
it_behaves_like 'Rpush::Client::ActiveRecord::Notification'
|
|
8
|
+
|
|
9
|
+
it "should validate the length of the binary conversion of the notification", :aggregate_failures do
|
|
10
|
+
notification = described_class.new
|
|
11
|
+
notification.app = Rpush::Apnsp8::App.create(apn_key: "1",
|
|
12
|
+
apn_key_id: "2",
|
|
13
|
+
name: 'test',
|
|
14
|
+
environment: 'development',
|
|
15
|
+
team_id: "3",
|
|
16
|
+
bundle_id: "4")
|
|
17
|
+
notification.device_token = "a" * 108
|
|
18
|
+
notification.alert = ""
|
|
19
|
+
|
|
20
|
+
notification.alert << "a" until notification.payload.bytesize == 4096
|
|
21
|
+
expect(notification.valid?).to be_truthy
|
|
22
|
+
expect(notification.errors[:base]).to be_empty
|
|
23
|
+
|
|
24
|
+
notification.alert << "a"
|
|
25
|
+
expect(notification.valid?).to be_falsey
|
|
26
|
+
expect(notification.errors[:base].include?("APN notification cannot be larger than 4096 bytes. Try condensing your alert and device attributes.")).to be_truthy
|
|
27
|
+
end
|
|
28
|
+
end if active_record?
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require "unit_spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Rpush::Client::Redis::Apnsp8::Notification do
|
|
4
|
+
after do
|
|
5
|
+
Rpush::Apnsp8::App.all.select { |a| a.environment == "development" && a.apn_key == "1" }.each(&:destroy)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it_behaves_like "Rpush::Client::Apns::Notification"
|
|
9
|
+
|
|
10
|
+
it "should validate the length of the binary conversion of the notification", :aggregate_failures do
|
|
11
|
+
notification = described_class.new
|
|
12
|
+
notification.app = Rpush::Apnsp8::App.create(apn_key: "1",
|
|
13
|
+
apn_key_id: "2",
|
|
14
|
+
name: 'test',
|
|
15
|
+
environment: 'development',
|
|
16
|
+
team_id: "3",
|
|
17
|
+
bundle_id: "4")
|
|
18
|
+
notification.device_token = "a" * 108
|
|
19
|
+
notification.alert = ""
|
|
20
|
+
|
|
21
|
+
notification.alert << "a" until notification.payload.bytesize == 4096
|
|
22
|
+
expect(notification.valid?).to be_truthy
|
|
23
|
+
expect(notification.errors[:base]).to be_empty
|
|
24
|
+
|
|
25
|
+
notification.alert << "a"
|
|
26
|
+
expect(notification.valid?).to be_falsey
|
|
27
|
+
expect(notification.errors[:base].include?("APN notification cannot be larger than 4096 bytes. Try condensing your alert and device attributes.")).to be_truthy
|
|
28
|
+
end
|
|
29
|
+
end if redis?
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'unit_spec_helper'
|
|
2
|
+
|
|
3
|
+
shared_examples 'Rpush::Client::Webpush::App' do
|
|
4
|
+
describe 'validates' do
|
|
5
|
+
subject { described_class.new }
|
|
6
|
+
|
|
7
|
+
it 'validates presence of name' do
|
|
8
|
+
is_expected.not_to be_valid
|
|
9
|
+
expect(subject.errors[:name]).to eq ["can't be blank"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'validates presence of vapid_keypair' do
|
|
13
|
+
is_expected.not_to be_valid
|
|
14
|
+
expect(subject.errors[:vapid_keypair]).to eq ["can't be blank"]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should require the vapid keypair to have subject, public and private key' do
|
|
18
|
+
subject.vapid_keypair = {}.to_json
|
|
19
|
+
is_expected.not_to be_valid
|
|
20
|
+
expect(subject.errors[:vapid_keypair].sort).to eq [
|
|
21
|
+
'must have a private_key entry',
|
|
22
|
+
'must have a public_key entry',
|
|
23
|
+
'must have a subject entry',
|
|
24
|
+
]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'should require valid json for the keypair' do
|
|
28
|
+
subject.vapid_keypair = 'invalid'
|
|
29
|
+
is_expected.not_to be_valid
|
|
30
|
+
expect(subject.errors[:vapid_keypair].sort).to eq [ 'must be valid JSON' ]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require 'unit_spec_helper'
|
|
2
|
+
|
|
3
|
+
shared_examples 'Rpush::Client::Webpush::Notification' do
|
|
4
|
+
subject(:notification) { described_class.new }
|
|
5
|
+
|
|
6
|
+
describe 'notification attributes' do
|
|
7
|
+
describe 'data' do
|
|
8
|
+
subject { described_class.new(data: { message: 'test', urgency: 'normal' } ) }
|
|
9
|
+
it 'has a message' do
|
|
10
|
+
expect(subject.message).to eq "test"
|
|
11
|
+
end
|
|
12
|
+
it 'has an urgency' do
|
|
13
|
+
expect(subject.urgency).to eq "normal"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe 'subscription' do
|
|
18
|
+
let(:subscription){ { endpoint: 'https://push.example.org/foo', keys: {'foo' => 'bar'}} }
|
|
19
|
+
subject { described_class.new(registration_ids: [subscription]) }
|
|
20
|
+
|
|
21
|
+
it "has a subscription" do
|
|
22
|
+
expect(subject.subscription).to eq({ endpoint: 'https://push.example.org/foo', keys: {foo: 'bar'} })
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
describe 'validates' do
|
|
29
|
+
let(:app) { Rpush::Webpush::App.create!(name: 'MyApp', vapid_keypair: VAPID_KEYPAIR) }
|
|
30
|
+
|
|
31
|
+
describe 'data' do
|
|
32
|
+
subject { described_class.new(app: app, registration_ids: [{endpoint: 'https://push.example.org/foo', keys: {'foo' => 'bar'}}]) }
|
|
33
|
+
it 'validates presence' do
|
|
34
|
+
is_expected.not_to be_valid
|
|
35
|
+
expect(subject.errors[:data]).to eq ["can't be blank"]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "has a 'data' payload limit of 4096 bytes" do
|
|
39
|
+
subject.data = { message: 'a' * 4096 }
|
|
40
|
+
is_expected.not_to be_valid
|
|
41
|
+
expected_errors = ["Notification payload data cannot be larger than 4096 bytes."]
|
|
42
|
+
expect(subject.errors[:base]).to eq expected_errors
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe 'registration_ids' do
|
|
47
|
+
subject { described_class.new(app: app, data: { message: 'test' }) }
|
|
48
|
+
it 'validates presence' do
|
|
49
|
+
is_expected.not_to be_valid
|
|
50
|
+
expect(subject.errors[:registration_ids]).to eq ["can't be blank"]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'limits the number of registration ids to exactly 1' do
|
|
54
|
+
subject.registration_ids = [{endpoint: 'string', keys: { 'a' => 'hash' }}] * 2
|
|
55
|
+
is_expected.not_to be_valid
|
|
56
|
+
expected_errors = ["Number of registration_ids cannot be larger than 1."]
|
|
57
|
+
expect(subject.errors[:base]).to eq expected_errors
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'validates the structure of the registration' do
|
|
61
|
+
subject.registration_ids = ['a']
|
|
62
|
+
is_expected.not_to be_valid
|
|
63
|
+
expect(subject.errors[:base]).to eq [
|
|
64
|
+
"Registration must have :endpoint (String) and :keys (Hash) keys"
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
subject.registration_ids = [{endpoint: 'string', keys: { 'a' => 'hash' }}]
|
|
68
|
+
is_expected.to be_valid
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe 'time_to_live' do
|
|
73
|
+
subject { described_class.new(app: app, data: { message: 'test' }, registration_ids: [{endpoint: 'https://push.example.org/foo', keys: {'foo' => 'bar'}}]) }
|
|
74
|
+
|
|
75
|
+
it 'should be > 0' do
|
|
76
|
+
subject.time_to_live = -1
|
|
77
|
+
is_expected.not_to be_valid
|
|
78
|
+
expect(subject.errors[:time_to_live]).to eq ['must be greater than 0']
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require 'unit_spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Rpush::Daemon::Webpush::Delivery do
|
|
4
|
+
let(:app) { Rpush::Webpush::App.create!(name: 'MyApp', vapid_keypair: VAPID_KEYPAIR) }
|
|
5
|
+
|
|
6
|
+
# Push subscription information as received from a client browser when the
|
|
7
|
+
# user subscribed to push notifications.
|
|
8
|
+
let(:device_reg) {
|
|
9
|
+
{ endpoint: 'https://webpush-provider.example.org/push/some-id',
|
|
10
|
+
keys: {'auth' => 'DgN9EBia1o057BdhCOGURA', 'p256dh' => 'BAtxJ--7vHq9IVm8utUB3peJ4lpxRqk1rukCIkVJOomS83QkCnrQ4EyYQsSaCRgy_c8XPytgXxuyAvRJdnTPK4A'} }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let(:data) { { message: 'some message' } }
|
|
14
|
+
let(:notification) { Rpush::Webpush::Notification.create!(app: app, registration_ids: [device_reg], data: data) }
|
|
15
|
+
let(:batch) { instance_double('Rpush::Daemon::Batch', notification_processed: nil) }
|
|
16
|
+
let(:response) { instance_double('Net::HTTPResponse', code: response_code, header: response_header, body: response_body) }
|
|
17
|
+
let(:response_code) { 201 }
|
|
18
|
+
let(:response_header) { {} }
|
|
19
|
+
let(:response_body) { nil }
|
|
20
|
+
let(:http) { instance_double('Net::HTTP::Persistent', request: response) }
|
|
21
|
+
let(:logger) { instance_double('Rpush::Logger', error: nil, info: nil, warn: nil, internal_logger: nil) }
|
|
22
|
+
let(:now) { Time.parse('2020-10-13 00:00:00 UTC') }
|
|
23
|
+
|
|
24
|
+
before do
|
|
25
|
+
allow(Rpush).to receive_messages(logger: logger)
|
|
26
|
+
allow(Time).to receive_messages(now: now)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
subject(:delivery) { described_class.new(app, http, notification, batch) }
|
|
30
|
+
|
|
31
|
+
describe '#perform' do
|
|
32
|
+
shared_examples 'process notification' do
|
|
33
|
+
it 'invoke batch.notification_processed' do
|
|
34
|
+
subject.perform rescue nil
|
|
35
|
+
expect(batch).to have_received(:notification_processed)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'when response code is 201' do
|
|
40
|
+
before do
|
|
41
|
+
allow(batch).to receive(:mark_delivered)
|
|
42
|
+
Rpush::Daemon.store = Rpush::Daemon::Store.const_get(Rpush.config.client.to_s.camelcase).new
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'marks the notification as delivered' do
|
|
46
|
+
delivery.perform
|
|
47
|
+
expect(batch).to have_received(:mark_delivered).with(notification)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it_behaves_like 'process notification'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
shared_examples 'retry delivery' do |response_code:|
|
|
54
|
+
let(:response_code) { response_code }
|
|
55
|
+
|
|
56
|
+
shared_examples 'logs' do |deliver_after:|
|
|
57
|
+
let(:expected_log_message) do
|
|
58
|
+
"[MyApp] Webpush endpoint responded with a #{response_code} error. Notification #{notification.id} will be retried after #{deliver_after} (retry 1)."
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'logs that the notification will be retried' do
|
|
62
|
+
delivery.perform
|
|
63
|
+
expect(logger).to have_received(:info).with(expected_log_message)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
context 'when Retry-After header is present' do
|
|
68
|
+
let(:response_header) { { 'retry-after' => 10 } }
|
|
69
|
+
|
|
70
|
+
before do
|
|
71
|
+
allow(delivery).to receive(:mark_retryable) do
|
|
72
|
+
notification.deliver_after = now + 10.seconds
|
|
73
|
+
notification.retries += 1
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'retry the notification' do
|
|
78
|
+
delivery.perform
|
|
79
|
+
expect(delivery).to have_received(:mark_retryable).with(notification, now + 10.seconds)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it_behaves_like 'logs', deliver_after: '2020-10-13 00:00:10'
|
|
83
|
+
it_behaves_like 'process notification'
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context 'when Retry-After header is not present' do
|
|
87
|
+
before do
|
|
88
|
+
allow(delivery).to receive(:mark_retryable_exponential) do
|
|
89
|
+
notification.deliver_after = now + 2.seconds
|
|
90
|
+
notification.retries = 1
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'retry the notification' do
|
|
95
|
+
delivery.perform
|
|
96
|
+
expect(delivery).to have_received(:mark_retryable_exponential).with(notification)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it_behaves_like 'logs', deliver_after: '2020-10-13 00:00:02'
|
|
100
|
+
it_behaves_like 'process notification'
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it_behaves_like 'retry delivery', response_code: 429
|
|
105
|
+
it_behaves_like 'retry delivery', response_code: 500
|
|
106
|
+
it_behaves_like 'retry delivery', response_code: 502
|
|
107
|
+
it_behaves_like 'retry delivery', response_code: 503
|
|
108
|
+
it_behaves_like 'retry delivery', response_code: 504
|
|
109
|
+
|
|
110
|
+
context 'when delivery failed' do
|
|
111
|
+
let(:response_code) { 400 }
|
|
112
|
+
let(:fail_message) { 'that was a bad request' }
|
|
113
|
+
before do
|
|
114
|
+
allow(response).to receive(:body) { fail_message }
|
|
115
|
+
allow(batch).to receive(:mark_failed)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'marks the notification as failed' do
|
|
119
|
+
expect { delivery.perform }.to raise_error(Rpush::DeliveryError)
|
|
120
|
+
expected_message = "Unable to deliver notification #{notification.id}, " \
|
|
121
|
+
"received error 400 (Bad Request: #{fail_message})"
|
|
122
|
+
expect(batch).to have_received(:mark_failed).with(notification, 400, expected_message)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it_behaves_like 'process notification'
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context 'when SocketError raised' do
|
|
129
|
+
before do
|
|
130
|
+
allow(http).to receive(:request) { raise SocketError }
|
|
131
|
+
allow(delivery).to receive(:mark_retryable)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'retry delivery after 10 seconds' do
|
|
135
|
+
expect { delivery.perform }.to raise_error(SocketError)
|
|
136
|
+
expect(delivery).to have_received(:mark_retryable).with(notification, now + 10.seconds, SocketError)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it_behaves_like 'process notification'
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rpush
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ian Leitch
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-01-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: multi_json
|
|
@@ -134,6 +134,20 @@ dependencies:
|
|
|
134
134
|
- - ">="
|
|
135
135
|
- !ruby/object:Gem::Version
|
|
136
136
|
version: '0'
|
|
137
|
+
- !ruby/object:Gem::Dependency
|
|
138
|
+
name: webpush
|
|
139
|
+
requirement: !ruby/object:Gem::Requirement
|
|
140
|
+
requirements:
|
|
141
|
+
- - "~>"
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: '1.0'
|
|
144
|
+
type: :runtime
|
|
145
|
+
prerelease: false
|
|
146
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
147
|
+
requirements:
|
|
148
|
+
- - "~>"
|
|
149
|
+
- !ruby/object:Gem::Version
|
|
150
|
+
version: '1.0'
|
|
137
151
|
- !ruby/object:Gem::Dependency
|
|
138
152
|
name: rake
|
|
139
153
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -423,6 +437,8 @@ files:
|
|
|
423
437
|
- lib/rpush/client/active_model/pushy/notification.rb
|
|
424
438
|
- lib/rpush/client/active_model/pushy/time_to_live_validator.rb
|
|
425
439
|
- lib/rpush/client/active_model/registration_ids_count_validator.rb
|
|
440
|
+
- lib/rpush/client/active_model/webpush/app.rb
|
|
441
|
+
- lib/rpush/client/active_model/webpush/notification.rb
|
|
426
442
|
- lib/rpush/client/active_model/wns/app.rb
|
|
427
443
|
- lib/rpush/client/active_model/wns/notification.rb
|
|
428
444
|
- lib/rpush/client/active_model/wpns/app.rb
|
|
@@ -444,6 +460,8 @@ files:
|
|
|
444
460
|
- lib/rpush/client/active_record/notification.rb
|
|
445
461
|
- lib/rpush/client/active_record/pushy/app.rb
|
|
446
462
|
- lib/rpush/client/active_record/pushy/notification.rb
|
|
463
|
+
- lib/rpush/client/active_record/webpush/app.rb
|
|
464
|
+
- lib/rpush/client/active_record/webpush/notification.rb
|
|
447
465
|
- lib/rpush/client/active_record/wns/app.rb
|
|
448
466
|
- lib/rpush/client/active_record/wns/badge_notification.rb
|
|
449
467
|
- lib/rpush/client/active_record/wns/notification.rb
|
|
@@ -466,6 +484,8 @@ files:
|
|
|
466
484
|
- lib/rpush/client/redis/notification.rb
|
|
467
485
|
- lib/rpush/client/redis/pushy/app.rb
|
|
468
486
|
- lib/rpush/client/redis/pushy/notification.rb
|
|
487
|
+
- lib/rpush/client/redis/webpush/app.rb
|
|
488
|
+
- lib/rpush/client/redis/webpush/notification.rb
|
|
469
489
|
- lib/rpush/client/redis/wns/app.rb
|
|
470
490
|
- lib/rpush/client/redis/wns/badge_notification.rb
|
|
471
491
|
- lib/rpush/client/redis/wns/notification.rb
|
|
@@ -520,6 +540,8 @@ files:
|
|
|
520
540
|
- lib/rpush/daemon/string_helpers.rb
|
|
521
541
|
- lib/rpush/daemon/synchronizer.rb
|
|
522
542
|
- lib/rpush/daemon/tcp_connection.rb
|
|
543
|
+
- lib/rpush/daemon/webpush.rb
|
|
544
|
+
- lib/rpush/daemon/webpush/delivery.rb
|
|
523
545
|
- lib/rpush/daemon/wns.rb
|
|
524
546
|
- lib/rpush/daemon/wns/badge_request.rb
|
|
525
547
|
- lib/rpush/daemon/wns/delivery.rb
|
|
@@ -552,6 +574,7 @@ files:
|
|
|
552
574
|
- spec/functional/pushy_spec.rb
|
|
553
575
|
- spec/functional/retry_spec.rb
|
|
554
576
|
- spec/functional/synchronization_spec.rb
|
|
577
|
+
- spec/functional/webpush_spec.rb
|
|
555
578
|
- spec/functional/wpns_spec.rb
|
|
556
579
|
- spec/functional_spec_helper.rb
|
|
557
580
|
- spec/spec_helper.rb
|
|
@@ -570,6 +593,7 @@ files:
|
|
|
570
593
|
- spec/unit/client/active_record/apns/notification_spec.rb
|
|
571
594
|
- spec/unit/client/active_record/apns2/app_spec.rb
|
|
572
595
|
- spec/unit/client/active_record/apns2/notification_spec.rb
|
|
596
|
+
- spec/unit/client/active_record/apnsp8/notification_spec.rb
|
|
573
597
|
- spec/unit/client/active_record/app_spec.rb
|
|
574
598
|
- spec/unit/client/active_record/gcm/app_spec.rb
|
|
575
599
|
- spec/unit/client/active_record/gcm/notification_spec.rb
|
|
@@ -578,6 +602,8 @@ files:
|
|
|
578
602
|
- spec/unit/client/active_record/pushy/notification_spec.rb
|
|
579
603
|
- spec/unit/client/active_record/shared/app.rb
|
|
580
604
|
- spec/unit/client/active_record/shared/notification.rb
|
|
605
|
+
- spec/unit/client/active_record/webpush/app_spec.rb
|
|
606
|
+
- spec/unit/client/active_record/webpush/notification_spec.rb
|
|
581
607
|
- spec/unit/client/active_record/wns/badge_notification_spec.rb
|
|
582
608
|
- spec/unit/client/active_record/wns/raw_notification_spec.rb
|
|
583
609
|
- spec/unit/client/active_record/wpns/app_spec.rb
|
|
@@ -589,12 +615,15 @@ files:
|
|
|
589
615
|
- spec/unit/client/redis/apns/notification_spec.rb
|
|
590
616
|
- spec/unit/client/redis/apns2/app_spec.rb
|
|
591
617
|
- spec/unit/client/redis/apns2/notification_spec.rb
|
|
618
|
+
- spec/unit/client/redis/apnsp8/notification_spec.rb
|
|
592
619
|
- spec/unit/client/redis/app_spec.rb
|
|
593
620
|
- spec/unit/client/redis/gcm/app_spec.rb
|
|
594
621
|
- spec/unit/client/redis/gcm/notification_spec.rb
|
|
595
622
|
- spec/unit/client/redis/notification_spec.rb
|
|
596
623
|
- spec/unit/client/redis/pushy/app_spec.rb
|
|
597
624
|
- spec/unit/client/redis/pushy/notification_spec.rb
|
|
625
|
+
- spec/unit/client/redis/webpush/app_spec.rb
|
|
626
|
+
- spec/unit/client/redis/webpush/notification_spec.rb
|
|
598
627
|
- spec/unit/client/redis/wns/badge_notification_spec.rb
|
|
599
628
|
- spec/unit/client/redis/wns/raw_notification_spec.rb
|
|
600
629
|
- spec/unit/client/redis/wpns/app_spec.rb
|
|
@@ -610,6 +639,8 @@ files:
|
|
|
610
639
|
- spec/unit/client/shared/notification.rb
|
|
611
640
|
- spec/unit/client/shared/pushy/app.rb
|
|
612
641
|
- spec/unit/client/shared/pushy/notification.rb
|
|
642
|
+
- spec/unit/client/shared/webpush/app.rb
|
|
643
|
+
- spec/unit/client/shared/webpush/notification.rb
|
|
613
644
|
- spec/unit/client/shared/wns/badge_notification.rb
|
|
614
645
|
- spec/unit/client/shared/wns/raw_notification.rb
|
|
615
646
|
- spec/unit/client/shared/wpns/app.rb
|
|
@@ -638,6 +669,7 @@ files:
|
|
|
638
669
|
- spec/unit/daemon/store/active_record_spec.rb
|
|
639
670
|
- spec/unit/daemon/store/redis_spec.rb
|
|
640
671
|
- spec/unit/daemon/tcp_connection_spec.rb
|
|
672
|
+
- spec/unit/daemon/webpush/delivery_spec.rb
|
|
641
673
|
- spec/unit/daemon/wns/delivery_spec.rb
|
|
642
674
|
- spec/unit/daemon/wns/post_request_spec.rb
|
|
643
675
|
- spec/unit/daemon/wpns/delivery_spec.rb
|
|
@@ -695,6 +727,7 @@ test_files:
|
|
|
695
727
|
- spec/functional/pushy_spec.rb
|
|
696
728
|
- spec/functional/retry_spec.rb
|
|
697
729
|
- spec/functional/synchronization_spec.rb
|
|
730
|
+
- spec/functional/webpush_spec.rb
|
|
698
731
|
- spec/functional/wpns_spec.rb
|
|
699
732
|
- spec/functional_spec_helper.rb
|
|
700
733
|
- spec/spec_helper.rb
|
|
@@ -713,6 +746,7 @@ test_files:
|
|
|
713
746
|
- spec/unit/client/active_record/apns/notification_spec.rb
|
|
714
747
|
- spec/unit/client/active_record/apns2/app_spec.rb
|
|
715
748
|
- spec/unit/client/active_record/apns2/notification_spec.rb
|
|
749
|
+
- spec/unit/client/active_record/apnsp8/notification_spec.rb
|
|
716
750
|
- spec/unit/client/active_record/app_spec.rb
|
|
717
751
|
- spec/unit/client/active_record/gcm/app_spec.rb
|
|
718
752
|
- spec/unit/client/active_record/gcm/notification_spec.rb
|
|
@@ -721,6 +755,8 @@ test_files:
|
|
|
721
755
|
- spec/unit/client/active_record/pushy/notification_spec.rb
|
|
722
756
|
- spec/unit/client/active_record/shared/app.rb
|
|
723
757
|
- spec/unit/client/active_record/shared/notification.rb
|
|
758
|
+
- spec/unit/client/active_record/webpush/app_spec.rb
|
|
759
|
+
- spec/unit/client/active_record/webpush/notification_spec.rb
|
|
724
760
|
- spec/unit/client/active_record/wns/badge_notification_spec.rb
|
|
725
761
|
- spec/unit/client/active_record/wns/raw_notification_spec.rb
|
|
726
762
|
- spec/unit/client/active_record/wpns/app_spec.rb
|
|
@@ -732,12 +768,15 @@ test_files:
|
|
|
732
768
|
- spec/unit/client/redis/apns/notification_spec.rb
|
|
733
769
|
- spec/unit/client/redis/apns2/app_spec.rb
|
|
734
770
|
- spec/unit/client/redis/apns2/notification_spec.rb
|
|
771
|
+
- spec/unit/client/redis/apnsp8/notification_spec.rb
|
|
735
772
|
- spec/unit/client/redis/app_spec.rb
|
|
736
773
|
- spec/unit/client/redis/gcm/app_spec.rb
|
|
737
774
|
- spec/unit/client/redis/gcm/notification_spec.rb
|
|
738
775
|
- spec/unit/client/redis/notification_spec.rb
|
|
739
776
|
- spec/unit/client/redis/pushy/app_spec.rb
|
|
740
777
|
- spec/unit/client/redis/pushy/notification_spec.rb
|
|
778
|
+
- spec/unit/client/redis/webpush/app_spec.rb
|
|
779
|
+
- spec/unit/client/redis/webpush/notification_spec.rb
|
|
741
780
|
- spec/unit/client/redis/wns/badge_notification_spec.rb
|
|
742
781
|
- spec/unit/client/redis/wns/raw_notification_spec.rb
|
|
743
782
|
- spec/unit/client/redis/wpns/app_spec.rb
|
|
@@ -753,6 +792,8 @@ test_files:
|
|
|
753
792
|
- spec/unit/client/shared/notification.rb
|
|
754
793
|
- spec/unit/client/shared/pushy/app.rb
|
|
755
794
|
- spec/unit/client/shared/pushy/notification.rb
|
|
795
|
+
- spec/unit/client/shared/webpush/app.rb
|
|
796
|
+
- spec/unit/client/shared/webpush/notification.rb
|
|
756
797
|
- spec/unit/client/shared/wns/badge_notification.rb
|
|
757
798
|
- spec/unit/client/shared/wns/raw_notification.rb
|
|
758
799
|
- spec/unit/client/shared/wpns/app.rb
|
|
@@ -781,6 +822,7 @@ test_files:
|
|
|
781
822
|
- spec/unit/daemon/store/active_record_spec.rb
|
|
782
823
|
- spec/unit/daemon/store/redis_spec.rb
|
|
783
824
|
- spec/unit/daemon/tcp_connection_spec.rb
|
|
825
|
+
- spec/unit/daemon/webpush/delivery_spec.rb
|
|
784
826
|
- spec/unit/daemon/wns/delivery_spec.rb
|
|
785
827
|
- spec/unit/daemon/wns/post_request_spec.rb
|
|
786
828
|
- spec/unit/daemon/wpns/delivery_spec.rb
|