fcm-ruby-push-notifications 1.2.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 +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
|