fcm-ruby-push-notifications 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +37 -0
- data/.rspec +2 -0
- data/.travis.yml +19 -0
- data/CHANGELOG.md +32 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +82 -0
- data/Rakefile +12 -0
- data/examples/apns.rb +27 -0
- data/examples/gcm.rb +25 -0
- data/examples/mpns.rb +22 -0
- data/examples/wns.rb +25 -0
- data/gemfiles/Gemfile-legacy +8 -0
- data/lib/ruby-push-notifications.rb +7 -0
- data/lib/ruby-push-notifications/apns.rb +44 -0
- data/lib/ruby-push-notifications/apns/apns_connection.rb +70 -0
- data/lib/ruby-push-notifications/apns/apns_notification.rb +91 -0
- data/lib/ruby-push-notifications/apns/apns_pusher.rb +84 -0
- data/lib/ruby-push-notifications/apns/apns_results.rb +30 -0
- data/lib/ruby-push-notifications/fcm.rb +8 -0
- data/lib/ruby-push-notifications/fcm/fcm_connection.rb +60 -0
- data/lib/ruby-push-notifications/fcm/fcm_error.rb +17 -0
- data/lib/ruby-push-notifications/fcm/fcm_notification.rb +31 -0
- data/lib/ruby-push-notifications/fcm/fcm_pusher.rb +29 -0
- data/lib/ruby-push-notifications/fcm/fcm_request.rb +28 -0
- data/lib/ruby-push-notifications/fcm/fcm_response.rb +89 -0
- data/lib/ruby-push-notifications/fcm/fcm_result.rb +52 -0
- data/lib/ruby-push-notifications/gcm.rb +6 -0
- data/lib/ruby-push-notifications/gcm/gcm_connection.rb +54 -0
- data/lib/ruby-push-notifications/gcm/gcm_error.rb +17 -0
- data/lib/ruby-push-notifications/gcm/gcm_notification.rb +31 -0
- data/lib/ruby-push-notifications/gcm/gcm_pusher.rb +35 -0
- data/lib/ruby-push-notifications/gcm/gcm_response.rb +89 -0
- data/lib/ruby-push-notifications/gcm/gcm_result.rb +52 -0
- data/lib/ruby-push-notifications/mpns.rb +5 -0
- data/lib/ruby-push-notifications/mpns/mpns_connection.rb +87 -0
- data/lib/ruby-push-notifications/mpns/mpns_notification.rb +94 -0
- data/lib/ruby-push-notifications/mpns/mpns_pusher.rb +33 -0
- data/lib/ruby-push-notifications/mpns/mpns_response.rb +82 -0
- data/lib/ruby-push-notifications/mpns/mpns_result.rb +182 -0
- data/lib/ruby-push-notifications/notification_results_manager.rb +14 -0
- data/lib/ruby-push-notifications/wns.rb +6 -0
- data/lib/ruby-push-notifications/wns/wns_access.rb +82 -0
- data/lib/ruby-push-notifications/wns/wns_connection.rb +97 -0
- data/lib/ruby-push-notifications/wns/wns_notification.rb +101 -0
- data/lib/ruby-push-notifications/wns/wns_pusher.rb +32 -0
- data/lib/ruby-push-notifications/wns/wns_response.rb +83 -0
- data/lib/ruby-push-notifications/wns/wns_result.rb +208 -0
- data/ruby-push-notifications.gemspec +28 -0
- data/spec/factories.rb +17 -0
- data/spec/factories/notifications.rb +30 -0
- data/spec/ruby-push-notifications/apns/apns_connection_spec.rb +92 -0
- data/spec/ruby-push-notifications/apns/apns_notification_spec.rb +42 -0
- data/spec/ruby-push-notifications/apns/apns_pusher_spec.rb +295 -0
- data/spec/ruby-push-notifications/gcm/gcm_connection_spec.rb +46 -0
- data/spec/ruby-push-notifications/gcm/gcm_notification_spec.rb +37 -0
- data/spec/ruby-push-notifications/gcm/gcm_pusher_spec.rb +45 -0
- data/spec/ruby-push-notifications/gcm/gcm_response_spec.rb +82 -0
- data/spec/ruby-push-notifications/mpns/mpns_connection_spec.rb +46 -0
- data/spec/ruby-push-notifications/mpns/mpns_notification_spec.rb +53 -0
- data/spec/ruby-push-notifications/mpns/mpns_pusher_spec.rb +59 -0
- data/spec/ruby-push-notifications/mpns/mpns_response_spec.rb +64 -0
- data/spec/ruby-push-notifications/wns/wns_access_spec.rb +76 -0
- data/spec/ruby-push-notifications/wns/wns_connection_spec.rb +53 -0
- data/spec/ruby-push-notifications/wns/wns_notification_spec.rb +177 -0
- data/spec/ruby-push-notifications/wns/wns_pusher_spec.rb +58 -0
- data/spec/ruby-push-notifications/wns/wns_response_spec.rb +65 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/dummy.pem +44 -0
- data/spec/support/factory_girl.rb +5 -0
- data/spec/support/results_shared_examples.rb +31 -0
- metadata +249 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module RubyPushNotifications
|
4
|
+
module APNS
|
5
|
+
# Represents a APNS Notification.
|
6
|
+
# Manages the conversion of the notification to APNS binary format for
|
7
|
+
# each of the destinations.
|
8
|
+
# By default sets maximum expiration date (4 weeks).
|
9
|
+
#
|
10
|
+
# @author Carlos Alonso
|
11
|
+
class APNSNotification
|
12
|
+
include RubyPushNotifications::NotificationResultsManager
|
13
|
+
|
14
|
+
# @private. 4 weeks in seconds
|
15
|
+
WEEKS_4 = 2419200
|
16
|
+
|
17
|
+
# Initializes the APNS Notification
|
18
|
+
#
|
19
|
+
# @param [Array]. Array containing all destinations for the notification
|
20
|
+
# @param [Hash]. Hash with the data to use as payload.
|
21
|
+
def initialize(tokens, data)
|
22
|
+
@tokens = tokens
|
23
|
+
@data = data
|
24
|
+
end
|
25
|
+
|
26
|
+
# Method that yields the notification's binary for each of the receivers.
|
27
|
+
#
|
28
|
+
# @param starting_id [Integer]. Every notification encodes a unique ID for
|
29
|
+
# further reference. This parameter represents the first id the first
|
30
|
+
# notification of this group should use.
|
31
|
+
# @yieldparam [String]. APNS binary's representation of this notification.
|
32
|
+
# Consisting of:
|
33
|
+
# Notification = 2(1), FrameLength(4), items(FrameLength)
|
34
|
+
# Item = ItemID(1), ItemLength(2), data(ItemLength)
|
35
|
+
# Items:
|
36
|
+
# Device Token => Id: 1, length: 32, data: binary device token
|
37
|
+
# Payload => Id: 2, length: ??, data: json formatted payload
|
38
|
+
# Notification ID => Id: 3, length: 4, data: notif id as int
|
39
|
+
# Expiration Date => Id: 4, length: 4, data: Unix timestamp as int
|
40
|
+
# Priority => Id: 5, length: 1, data: 10 as 1 byte int
|
41
|
+
# (https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW4)
|
42
|
+
def each_message(starting_id)
|
43
|
+
@tokens.each_with_index do |token, i|
|
44
|
+
bytes = device_token(token) + payload + notification_id(starting_id + i) + expiration_date + priority
|
45
|
+
yield [2, bytes.bytesize, bytes].pack 'cNa*'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Integer]. The number of binaries this notification will send.
|
50
|
+
# One for each receiver.
|
51
|
+
def count
|
52
|
+
@tokens.count
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# @param [String]. The device token to encode.
|
58
|
+
# @return [String]. Binary representation of the device token field.
|
59
|
+
def device_token(token)
|
60
|
+
[1, 32, token].pack 'cnH64'
|
61
|
+
end
|
62
|
+
|
63
|
+
# Generates the APNS's binary representation of the notification's payload.
|
64
|
+
# Caches the value in an instance variable.
|
65
|
+
#
|
66
|
+
# @return [String]. Binary representation of the notification's payload.
|
67
|
+
def payload
|
68
|
+
@encoded_payload ||= -> {
|
69
|
+
json = (@data.respond_to?(:to_json) ? @data.to_json : JSON.dump(@data)).force_encoding 'ascii-8bit'
|
70
|
+
[2, json.bytesize, json].pack 'cna*'
|
71
|
+
}.call
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param [Integer]. The unique ID for this notification.
|
75
|
+
# @return [String]. Binary representation of the notification id field.
|
76
|
+
def notification_id(id)
|
77
|
+
[3, 4, id].pack 'cnN'
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [String]. Binary representation of the expiration date field.
|
81
|
+
def expiration_date
|
82
|
+
[4, 4, (Time.now + WEEKS_4).to_i].pack 'cnN'
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [String]. Binary representation of the priority field.
|
86
|
+
def priority
|
87
|
+
[5, 1, 10].pack 'cnc'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module APNS
|
4
|
+
# This class coordinates the process of sending notifications.
|
5
|
+
# It takes care of reopening closed APNSConnections and seeking back to
|
6
|
+
# the failed notification to keep writing.
|
7
|
+
#
|
8
|
+
# Remember that APNS doesn't confirm successful notification, it just
|
9
|
+
# notifies when one went wrong and closes the connection. Therefore, this
|
10
|
+
# APNSPusher reconnects and rewinds the array until the notification that
|
11
|
+
# Apple rejected.
|
12
|
+
#
|
13
|
+
# @author Carlos Alonso
|
14
|
+
class APNSPusher
|
15
|
+
|
16
|
+
# @param certificate [String]. The PEM encoded APNS certificate.
|
17
|
+
# @param sandbox [Boolean]. Whether the certificate is an APNS sandbox or not.
|
18
|
+
# @param options [Hash] optional. Options for APNSPusher. Currently supports:
|
19
|
+
# * host [String]: Hostname of the APNS environment. Defaults to the official APNS hostname.
|
20
|
+
# * connect_timeout [Integer]: Number of seconds to wait for the connection to open. Defaults to 30.
|
21
|
+
def initialize(certificate, sandbox, password = nil, options = {})
|
22
|
+
@certificate = certificate
|
23
|
+
@pass = password
|
24
|
+
@sandbox = sandbox
|
25
|
+
@options = options
|
26
|
+
end
|
27
|
+
|
28
|
+
# Pushes the notifications.
|
29
|
+
# Builds an array with all the binaries (one for each notification and receiver)
|
30
|
+
# and pushes them sequentially to APNS monitoring the response.
|
31
|
+
# If an error is received, the connection is reopened and the process
|
32
|
+
# continues at the next notification after the failed one (pointed by the response error)
|
33
|
+
#
|
34
|
+
# For each notification assigns an array with the results of each submission.
|
35
|
+
#
|
36
|
+
# @param notifications [Array]. All the APNSNotifications to be sent.
|
37
|
+
def push(notifications)
|
38
|
+
conn = APNSConnection.open @certificate, @sandbox, @pass, @options
|
39
|
+
|
40
|
+
binaries = notifications.each_with_object([]) do |notif, binaries|
|
41
|
+
notif.each_message(binaries.count) do |msg|
|
42
|
+
binaries << msg
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
results = []
|
47
|
+
i = 0
|
48
|
+
while i < binaries.count
|
49
|
+
conn.write binaries[i]
|
50
|
+
|
51
|
+
if i == binaries.count-1
|
52
|
+
conn.flush
|
53
|
+
rs, = IO.select([conn], nil, nil, 2)
|
54
|
+
else
|
55
|
+
rs, = IO.select([conn], [conn])
|
56
|
+
end
|
57
|
+
if rs && rs.any?
|
58
|
+
packed = rs[0].read 6
|
59
|
+
if packed.nil? && i == 0
|
60
|
+
# The connection wasn't properly open
|
61
|
+
# Probably because of wrong certificate/sandbox? combination
|
62
|
+
results << UNKNOWN_ERROR_STATUS_CODE
|
63
|
+
else
|
64
|
+
err = packed.unpack 'ccN'
|
65
|
+
results.slice! err[2]..-1
|
66
|
+
results << err[1]
|
67
|
+
i = err[2]
|
68
|
+
conn = APNSConnection.open @certificate, @sandbox, @pass, @options
|
69
|
+
end
|
70
|
+
else
|
71
|
+
results << NO_ERROR_STATUS_CODE
|
72
|
+
end
|
73
|
+
i += 1
|
74
|
+
end
|
75
|
+
|
76
|
+
conn.close
|
77
|
+
|
78
|
+
notifications.each do |notif|
|
79
|
+
notif.results = APNSResults.new(results.slice! 0, notif.count)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RubyPushNotifications
|
2
|
+
module APNS
|
3
|
+
# This class is responsible for holding and
|
4
|
+
# managing result of a pushed notification.
|
5
|
+
#
|
6
|
+
# @author Carlos Alonso
|
7
|
+
class APNSResults
|
8
|
+
|
9
|
+
# @return [Array] of each destination's individual result.
|
10
|
+
attr_reader :individual_results
|
11
|
+
|
12
|
+
# Initializes the result
|
13
|
+
#
|
14
|
+
# @param [Array] containing each destination's individual result.
|
15
|
+
def initialize(results)
|
16
|
+
@individual_results = results
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Integer] numer of successfully pushed notifications
|
20
|
+
def success
|
21
|
+
@success ||= individual_results.count { |r| r == NO_ERROR_STATUS_CODE }
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Integer] number of failed notifications
|
25
|
+
def failed
|
26
|
+
@failed ||= individual_results.count { |r| r != NO_ERROR_STATUS_CODE }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'ruby-push-notifications/fcm/fcm_request'
|
2
|
+
require 'ruby-push-notifications/fcm/fcm_connection'
|
3
|
+
require 'ruby-push-notifications/fcm/fcm_notification'
|
4
|
+
require 'ruby-push-notifications/fcm/fcm_pusher'
|
5
|
+
require 'ruby-push-notifications/fcm/fcm_response'
|
6
|
+
require 'ruby-push-notifications/fcm/fcm_error'
|
7
|
+
require 'ruby-push-notifications/fcm/fcm_result'
|
8
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
require 'httparty'
|
5
|
+
require 'cgi'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
module RubyPushNotifications
|
9
|
+
module FCM
|
10
|
+
# Encapsulates a connection to the FCM service
|
11
|
+
# Responsible for final connection with the service.
|
12
|
+
#
|
13
|
+
# @author Carlos Alonso
|
14
|
+
class FCMConnection
|
15
|
+
|
16
|
+
# @private The URL of the Android FCM endpoint
|
17
|
+
FCM_URL = 'https://fcm.googleapis.com/fcm'
|
18
|
+
|
19
|
+
# @private Content-Type HTTP Header string
|
20
|
+
CONTENT_TYPE_HEADER = 'Content-Type'
|
21
|
+
|
22
|
+
# @private Application/JSON content type
|
23
|
+
JSON_CONTENT_TYPE = 'application/json'
|
24
|
+
|
25
|
+
# @private Authorization HTTP Header String
|
26
|
+
AUTHORIZATION_HEADER = 'Authorization'
|
27
|
+
GROUP_NOTIFICATION_BASE_URI = 'https://android.googleapis.com/gcm'
|
28
|
+
|
29
|
+
# Issues a POST request to the FCM send endpoint to
|
30
|
+
# submit the given notifications.
|
31
|
+
#
|
32
|
+
# @param notification [String]. The text to POST
|
33
|
+
# @param key [String]. The FCM sender id to use
|
34
|
+
# (https://developer.android.com/google/gcm/gcm.html#senderid)
|
35
|
+
# @return [FCMResponse]. The FCMResponse that encapsulates the received response
|
36
|
+
def self.post(notification, key)
|
37
|
+
headers = {
|
38
|
+
CONTENT_TYPE_HEADER => JSON_CONTENT_TYPE,
|
39
|
+
AUTHORIZATION_HEADER => "key=#{key}"
|
40
|
+
}
|
41
|
+
# TODO: remove to_json if causing error
|
42
|
+
params = {
|
43
|
+
body: notification.to_json,
|
44
|
+
headers: {
|
45
|
+
AUTHORIZATION_HEADER => "key=#{key}",
|
46
|
+
CONTENT_TYPE_HEADER => JSON_CONTENT_TYPE
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
fcm_request = FCMRequest.new(params)
|
51
|
+
response = fcm_request.make_request
|
52
|
+
# puts '*' * 10
|
53
|
+
# puts response
|
54
|
+
# puts '*' * 10
|
55
|
+
FCMResponse.new response.code.to_i, response.body
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module FCM
|
4
|
+
# Base class for all FCM related errors
|
5
|
+
#
|
6
|
+
# @author Carlos Alonso
|
7
|
+
class FCMError < StandardError ; end
|
8
|
+
|
9
|
+
class MalformedFCMJSONError < FCMError ; end
|
10
|
+
|
11
|
+
class FCMAuthError < FCMError ; end
|
12
|
+
|
13
|
+
class FCMInternalError < FCMError ; end
|
14
|
+
|
15
|
+
class UnexpectedFCMResponseError < FCMError ; end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module FCM
|
4
|
+
# Encapsulates a GCM Notification.
|
5
|
+
# By default only Required fields are set.
|
6
|
+
# (https://developer.android.com/google/gcm/server-ref.html#send-downstream)
|
7
|
+
#
|
8
|
+
# @author Carlos Alonso
|
9
|
+
class FCMNotification
|
10
|
+
include RubyPushNotifications::NotificationResultsManager
|
11
|
+
|
12
|
+
# Initializes the notification
|
13
|
+
#
|
14
|
+
# @param [Array]. Array with the receiver's GCM registration ids.
|
15
|
+
# @param [Hash]. Payload to send.
|
16
|
+
def initialize(registration_ids, data)
|
17
|
+
@registration_ids = registration_ids
|
18
|
+
@data = data
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [String]. The GCM's JSON format for the payload to send.
|
22
|
+
# (https://developer.android.com/google/gcm/server-ref.html#send-downstream)
|
23
|
+
def as_fcm_json
|
24
|
+
JSON.dump(
|
25
|
+
registration_ids: @registration_ids,
|
26
|
+
data: @data
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module FCM
|
4
|
+
|
5
|
+
# This class is responsible for sending notifications to the FCM service.
|
6
|
+
#
|
7
|
+
# @author Carlos Alonso
|
8
|
+
class FCMPusher
|
9
|
+
|
10
|
+
# Initializes the FCMPusher
|
11
|
+
#
|
12
|
+
# @param key [String]. FCM sender id to use
|
13
|
+
def initialize(key)
|
14
|
+
@key = key
|
15
|
+
end
|
16
|
+
|
17
|
+
# Actually pushes the given notifications.
|
18
|
+
# Assigns every notification an array with the result of each
|
19
|
+
# individual notification.
|
20
|
+
#
|
21
|
+
# @param notifications [Array]. Array of FCMNotification to send.
|
22
|
+
def push(notifications)
|
23
|
+
notifications.each do |notif|
|
24
|
+
notif.results = FCMConnection.post notif.as_fcm_json, @key
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'cgi'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module RubyPushNotifications
|
6
|
+
module FCM
|
7
|
+
# Encapsulates a connection to the FCM service
|
8
|
+
# Responsible for final connection with the service.
|
9
|
+
#
|
10
|
+
# @author Carlos Alonso
|
11
|
+
class FCMRequest
|
12
|
+
include HTTParty
|
13
|
+
default_timeout 30
|
14
|
+
format :json
|
15
|
+
|
16
|
+
base_uri 'https://fcm.googleapis.com/fcm'
|
17
|
+
|
18
|
+
def initialize(params = {})
|
19
|
+
@params = params
|
20
|
+
end
|
21
|
+
def make_request
|
22
|
+
puts '********** make_request *************'
|
23
|
+
puts @params
|
24
|
+
response = self.class.post('/send', @params)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module FCM
|
4
|
+
|
5
|
+
# This class encapsulates a response received from the FCM service
|
6
|
+
# and helps parsing and understanding the received meesages/codes.
|
7
|
+
#
|
8
|
+
# @author Carlos Alonso
|
9
|
+
class FCMResponse
|
10
|
+
|
11
|
+
# @return [Integer] the number of successfully sent notifications
|
12
|
+
attr_reader :success
|
13
|
+
|
14
|
+
# @return [Integer] the number of failed notifications
|
15
|
+
attr_reader :failed
|
16
|
+
|
17
|
+
# @return [Integer] the number of received canonical IDS
|
18
|
+
# (https://developer.android.com/google/gcm/server-ref.html#table4)
|
19
|
+
attr_reader :canonical_ids
|
20
|
+
|
21
|
+
# @return [Array] Array of a GCMResult for every receiver of the notification
|
22
|
+
# sent indicating the result of the operation.
|
23
|
+
attr_reader :results
|
24
|
+
alias_method :individual_results, :results
|
25
|
+
|
26
|
+
# Initializes the GCMResponse and runs response parsing
|
27
|
+
#
|
28
|
+
# @param code [Integer]. The HTTP status code received
|
29
|
+
# @param body [String]. The response body received.
|
30
|
+
# @raise MalformedGCMJsonError if code == 400 Bad Request
|
31
|
+
# @raise GCMAuthError if code == 401 Unauthorized
|
32
|
+
# @raise GCMInternalError if code == 5xx
|
33
|
+
# @raise UnexpectedGCMResponseError if code != 200
|
34
|
+
def initialize(code, body)
|
35
|
+
case code
|
36
|
+
when 200
|
37
|
+
parse_response body
|
38
|
+
when 400
|
39
|
+
raise MalformedFCMJSONError, body
|
40
|
+
when 401
|
41
|
+
raise FCMAuthError, body
|
42
|
+
when 500..599
|
43
|
+
raise FCMInternalError, body
|
44
|
+
else
|
45
|
+
raise UnexpectedFCMResponseError, code
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def ==(other)
|
50
|
+
(other.is_a?(FCMResponse) &&
|
51
|
+
success == other.success &&
|
52
|
+
failed == other.failed &&
|
53
|
+
canonical_ids == other.canonical_ids &&
|
54
|
+
results == other.results) || super(other)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Parses the response extracting counts for successful, failed and
|
60
|
+
# containing canonical ID messages.
|
61
|
+
# Also creates the results array assigning a GCMResult subclass for each
|
62
|
+
# registration ID the notification was sent to.
|
63
|
+
#
|
64
|
+
# @param body [String]. The response body
|
65
|
+
def parse_response(body)
|
66
|
+
json = JSON.parse body, symbolize_names: true
|
67
|
+
@success = json[:success]
|
68
|
+
@failed = json[:failure]
|
69
|
+
@canonical_ids = json[:canonical_ids]
|
70
|
+
@results = (json[:results] || []).map { |result| fcm_result_for result }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Factory method that, for each GCM result object assigns a GCMResult
|
74
|
+
# subclass.
|
75
|
+
#
|
76
|
+
# @param result [Hash]. GCM Result parsed hash
|
77
|
+
# @return [GCMResult]. Corresponding GCMResult subclass
|
78
|
+
def fcm_result_for(result)
|
79
|
+
if canonical_id = result[:registration_id]
|
80
|
+
FCMCanonicalIDResult.new canonical_id
|
81
|
+
elsif error = result[:error]
|
82
|
+
FCMResultError.new error
|
83
|
+
else
|
84
|
+
FCMResultOK.new
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|