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,33 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module MPNS
|
4
|
+
|
5
|
+
# This class is responsible for sending notifications to the MPNS service.
|
6
|
+
#
|
7
|
+
class MPNSPusher
|
8
|
+
|
9
|
+
# Initializes the MPNSPusher
|
10
|
+
#
|
11
|
+
# @param certificate [String]. The PEM encoded MPNS certificate.
|
12
|
+
# @param options [Hash] optional. Options for GCMPusher. Currently supports:
|
13
|
+
# * open_timeout [Integer]: Number of seconds to wait for the connection to open. Defaults to 30.
|
14
|
+
# * read_timeout [Integer]: Number of seconds to wait for one block to be read. Defaults to 30.
|
15
|
+
# (http://msdn.microsoft.com/pt-br/library/windows/apps/ff941099)
|
16
|
+
def initialize(certificate = nil, options = {})
|
17
|
+
@certificate = certificate
|
18
|
+
@options = options
|
19
|
+
end
|
20
|
+
|
21
|
+
# Actually pushes the given notifications.
|
22
|
+
# Assigns every notification an array with the result of each
|
23
|
+
# individual notification.
|
24
|
+
#
|
25
|
+
# @param notifications [Array]. Array of MPNSNotification to send.
|
26
|
+
def push(notifications)
|
27
|
+
notifications.each do |notif|
|
28
|
+
notif.results = MPNSConnection.post notif, @certificate, @options
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module MPNS
|
4
|
+
|
5
|
+
# This class encapsulates a response received from the MPNS service
|
6
|
+
# and helps parsing and understanding the received messages/codes.
|
7
|
+
#
|
8
|
+
class MPNSResponse
|
9
|
+
|
10
|
+
# @return [Integer] the number of successfully sent notifications
|
11
|
+
attr_reader :success
|
12
|
+
|
13
|
+
# @return [Integer] the number of failed notifications
|
14
|
+
attr_reader :failed
|
15
|
+
|
16
|
+
# @return [Array] Array of a MPNSResult for every receiver of the notification
|
17
|
+
# sent indicating the result of the operation.
|
18
|
+
attr_reader :results
|
19
|
+
alias_method :individual_results, :results
|
20
|
+
|
21
|
+
# Initializes the MPNSResponse and runs response parsing
|
22
|
+
#
|
23
|
+
# @param responses [Array]. Array with device_urls and http responses
|
24
|
+
def initialize(responses)
|
25
|
+
parse_response responses
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
(other.is_a?(MPNSResponse) &&
|
30
|
+
success == other.success &&
|
31
|
+
failed == other.failed &&
|
32
|
+
results == other.results) || super(other)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Parses the response extracting counts for successful, failed messages.
|
38
|
+
# Also creates the results array assigning a MPNSResult subclass for each
|
39
|
+
# device URL the notification was sent to.
|
40
|
+
#
|
41
|
+
# @param responses [Array]. Array of hash responses
|
42
|
+
def parse_response(responses)
|
43
|
+
@success = responses.count { |response| response[:code] == 200 }
|
44
|
+
@failed = responses.count { |response| response[:code] != 200 }
|
45
|
+
@results = responses.map do |response|
|
46
|
+
mpns_result_for response[:code],
|
47
|
+
response[:device_url],
|
48
|
+
response[:headers]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Factory method that, for each MPNS result object assigns a MPNSResult
|
53
|
+
# subclass.
|
54
|
+
#
|
55
|
+
# @param code [Integer]. The HTTP status code received
|
56
|
+
# @param device_url [String]. The receiver's MPNS device url.
|
57
|
+
# @param headers [Hash]. The HTTP headers received.
|
58
|
+
# @return [MPNSResult]. Corresponding MPNSResult subclass
|
59
|
+
def mpns_result_for(code, device_url, headers)
|
60
|
+
case code
|
61
|
+
when 200
|
62
|
+
MPNSResultOK.new device_url, headers
|
63
|
+
when 400
|
64
|
+
MalformedMPNSResultError.new device_url
|
65
|
+
when 401
|
66
|
+
MPNSAuthError.new device_url
|
67
|
+
when 404
|
68
|
+
MPNSInvalidError.new device_url, headers
|
69
|
+
when 406
|
70
|
+
MPNSLimitError.new device_url, headers
|
71
|
+
when 412
|
72
|
+
MPNSPreConditionError.new device_url, headers
|
73
|
+
when 500..599
|
74
|
+
MPNSInternalError.new device_url
|
75
|
+
else
|
76
|
+
MPNSResultError.new device_url
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
|
2
|
+
module RubyPushNotifications
|
3
|
+
module MPNS
|
4
|
+
# Class that encapsulates the result of a single sent notification to a single
|
5
|
+
# Device URL
|
6
|
+
# (https://msdn.microsoft.com/en-us/library/windows/apps/ff941100%28v=vs.105%29.aspx)
|
7
|
+
class MPNSResult
|
8
|
+
# @return [String]. Receiver MPNS device URL.
|
9
|
+
attr_accessor :device_url
|
10
|
+
|
11
|
+
# @private X-NotificationStatus HTTP Header string
|
12
|
+
X_NOTIFICATION_STATUS = 'x-notificationstatus'
|
13
|
+
|
14
|
+
# @private X-DeviceConnectionStatus HTTP Header string
|
15
|
+
X_DEVICE_CONNECTION_STATUS = 'x-deviceconnectionstatus'
|
16
|
+
|
17
|
+
# @private X-SubscriptionStatus HTTP Header string
|
18
|
+
X_SUBSCRIPTION_STATUS = 'x-subscriptionstatus'
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# Indicates that the notification was successfully sent to the corresponding
|
23
|
+
# device URL
|
24
|
+
class MPNSResultOK < MPNSResult
|
25
|
+
# @return [String]. The status of the notification received
|
26
|
+
# by the Microsoft Push Notification Service.
|
27
|
+
attr_accessor :notification_status
|
28
|
+
|
29
|
+
# @return [String]. The connection status of the device.
|
30
|
+
attr_accessor :device_connection_status
|
31
|
+
|
32
|
+
# @return [String]. The subscription status.
|
33
|
+
attr_accessor :subscription_status
|
34
|
+
|
35
|
+
def initialize(device_url, headers)
|
36
|
+
@device_url = device_url
|
37
|
+
@notification_status = headers[X_NOTIFICATION_STATUS]
|
38
|
+
@device_connection_status = headers[X_DEVICE_CONNECTION_STATUS]
|
39
|
+
@subscription_status = headers[X_SUBSCRIPTION_STATUS]
|
40
|
+
end
|
41
|
+
|
42
|
+
def ==(other)
|
43
|
+
(other.is_a?(MPNSResultOK) &&
|
44
|
+
device_url == other.device_url &&
|
45
|
+
notification_status == other.notification_status &&
|
46
|
+
device_connection_status == other.device_connection_status &&
|
47
|
+
subscription_status == other.subscription_status) || super(other)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# This error occurs when the cloud service sends a notification
|
52
|
+
# request with a bad XML document or malformed notification URI.
|
53
|
+
class MalformedMPNSResultError < MPNSResult
|
54
|
+
def initialize(device_url)
|
55
|
+
@device_url = device_url
|
56
|
+
end
|
57
|
+
|
58
|
+
def ==(other)
|
59
|
+
(other.is_a?(MalformedMPNSResultError) &&
|
60
|
+
device_url == other.device_url) || super(other)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Sending this notification is unauthorized.
|
65
|
+
class MPNSAuthError < MPNSResult
|
66
|
+
def initialize(device_url)
|
67
|
+
@device_url = device_url
|
68
|
+
end
|
69
|
+
|
70
|
+
def ==(other)
|
71
|
+
(other.is_a?(MPNSAuthError) &&
|
72
|
+
device_url == other.device_url) || super(other)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# The subscription is invalid and is not present on the Push Notification Service.
|
77
|
+
class MPNSInvalidError < MPNSResult
|
78
|
+
# @return [String]. The status of the notification received
|
79
|
+
# by the Microsoft Push Notification Service.
|
80
|
+
attr_accessor :notification_status
|
81
|
+
|
82
|
+
# @return [String]. The connection status of the device.
|
83
|
+
attr_accessor :device_connection_status
|
84
|
+
|
85
|
+
# @return [String]. The subscription status.
|
86
|
+
attr_accessor :subscription_status
|
87
|
+
|
88
|
+
def initialize(device_url, headers)
|
89
|
+
@device_url = device_url
|
90
|
+
@notification_status = headers[X_NOTIFICATION_STATUS]
|
91
|
+
@device_connection_status = headers[X_DEVICE_CONNECTION_STATUS]
|
92
|
+
@subscription_status = headers[X_SUBSCRIPTION_STATUS]
|
93
|
+
end
|
94
|
+
|
95
|
+
def ==(other)
|
96
|
+
(other.is_a?(MPNSInvalidError) &&
|
97
|
+
device_url == other.device_url &&
|
98
|
+
notification_status == other.notification_status &&
|
99
|
+
device_connection_status == other.device_connection_status &&
|
100
|
+
subscription_status == other.subscription_status) || super(other)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# This error occurs when an unauthenticated cloud service has reached
|
105
|
+
# the per-day throttling limit for a subscription,
|
106
|
+
# or when a cloud service (authenticated or unauthenticated)
|
107
|
+
# has sent too many notifications per second.
|
108
|
+
class MPNSLimitError < MPNSResult
|
109
|
+
# @return [String]. The status of the notification received
|
110
|
+
# by the Microsoft Push Notification Service.
|
111
|
+
attr_accessor :notification_status
|
112
|
+
|
113
|
+
# @return [String]. The connection status of the device.
|
114
|
+
attr_accessor :device_connection_status
|
115
|
+
|
116
|
+
# @return [String]. The subscription status.
|
117
|
+
attr_accessor :subscription_status
|
118
|
+
|
119
|
+
def initialize(device_url, headers)
|
120
|
+
@device_url = device_url
|
121
|
+
@notification_status = headers[X_NOTIFICATION_STATUS]
|
122
|
+
@device_connection_status = headers[X_DEVICE_CONNECTION_STATUS]
|
123
|
+
@subscription_status = headers[X_SUBSCRIPTION_STATUS]
|
124
|
+
end
|
125
|
+
|
126
|
+
def ==(other)
|
127
|
+
(other.is_a?(MPNSLimitError) &&
|
128
|
+
device_url == other.device_url &&
|
129
|
+
notification_status == other.notification_status &&
|
130
|
+
device_connection_status == other.device_connection_status &&
|
131
|
+
subscription_status == other.subscription_status) || super(other)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# The device is in a disconnected state.
|
136
|
+
class MPNSPreConditionError < MPNSResult
|
137
|
+
# @return [String]. The status of the notification received
|
138
|
+
# by the Microsoft Push Notification Service.
|
139
|
+
attr_accessor :notification_status
|
140
|
+
|
141
|
+
# @return [String]. The connection status of the device.
|
142
|
+
attr_accessor :device_connection_status
|
143
|
+
|
144
|
+
def initialize(device_url, headers)
|
145
|
+
@device_url = device_url
|
146
|
+
@notification_status = headers[X_NOTIFICATION_STATUS]
|
147
|
+
@device_connection_status = headers[X_DEVICE_CONNECTION_STATUS]
|
148
|
+
end
|
149
|
+
|
150
|
+
def ==(other)
|
151
|
+
(other.is_a?(MPNSPreConditionError) &&
|
152
|
+
device_url == other.device_url &&
|
153
|
+
notification_status == other.notification_status &&
|
154
|
+
device_connection_status == other.device_connection_status) || super(other)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# The Push Notification Service is unable to process the request.
|
159
|
+
class MPNSInternalError < MPNSResult
|
160
|
+
def initialize(device_url)
|
161
|
+
@device_url = device_url
|
162
|
+
end
|
163
|
+
|
164
|
+
def ==(other)
|
165
|
+
(other.is_a?(MPNSInternalError) &&
|
166
|
+
device_url == other.device_url) || super(other)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Unknow Error
|
171
|
+
class MPNSResultError < MPNSResult
|
172
|
+
def initialize(device_url)
|
173
|
+
@device_url = device_url
|
174
|
+
end
|
175
|
+
|
176
|
+
def ==(other)
|
177
|
+
(other.is_a?(MPNSResultError) &&
|
178
|
+
device_url == other.device_url) || super(other)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RubyPushNotifications
|
2
|
+
# This module contains the required behavior expected by particular notifications
|
3
|
+
#
|
4
|
+
# @author Carlos Alonso
|
5
|
+
module NotificationResultsManager
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@results, :success, :failed, :individual_results
|
9
|
+
|
10
|
+
# The corresponding object with the results from sending this notification
|
11
|
+
# that also will respond to #success, #failed and #individual_results
|
12
|
+
attr_accessor :results
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'ruby-push-notifications/wns/wns_access'
|
2
|
+
require 'ruby-push-notifications/wns/wns_connection'
|
3
|
+
require 'ruby-push-notifications/wns/wns_notification'
|
4
|
+
require 'ruby-push-notifications/wns/wns_pusher'
|
5
|
+
require 'ruby-push-notifications/wns/wns_result'
|
6
|
+
require 'ruby-push-notifications/wns/wns_response'
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/https'
|
3
|
+
require 'json'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module RubyPushNotifications
|
7
|
+
module WNS
|
8
|
+
# This class is responsible for get access auth token for sending pushes
|
9
|
+
#
|
10
|
+
class WNSAccess
|
11
|
+
|
12
|
+
# This class is responsible for structurize response from login WNS service
|
13
|
+
#
|
14
|
+
class Response
|
15
|
+
# @return [OpenStruct]. Return structurized response
|
16
|
+
attr_reader :response
|
17
|
+
|
18
|
+
def initialize(response)
|
19
|
+
@response = structurize(response)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def structurize(response)
|
25
|
+
body = response.body.to_s.empty? ? {} : JSON.parse(response.body)
|
26
|
+
OpenStruct.new(
|
27
|
+
status_code: response.code.to_i,
|
28
|
+
status: response.message,
|
29
|
+
error: body['error'],
|
30
|
+
error_description: body['error_description'],
|
31
|
+
access_token: body['access_token'],
|
32
|
+
token_ttl: body['expires_in']
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @private Grant type for getting access token
|
38
|
+
GRANT_TYPE = 'client_credentials'.freeze
|
39
|
+
|
40
|
+
# @private Scope for getting access token
|
41
|
+
SCOPE = 'notify.windows.com'.freeze
|
42
|
+
|
43
|
+
# @private Url for getting access token
|
44
|
+
ACCESS_TOKEN_URL = 'https://login.live.com/accesstoken.srf'.freeze
|
45
|
+
|
46
|
+
# @return [String]. Sid
|
47
|
+
attr_reader :sid
|
48
|
+
|
49
|
+
# @return [String]. Secret token
|
50
|
+
attr_reader :secret
|
51
|
+
|
52
|
+
# @param type [String]. Sid
|
53
|
+
# @param type [String]. Secret
|
54
|
+
#
|
55
|
+
# You can get it on https://account.live.com/developers/applications/index
|
56
|
+
def initialize(sid, secret)
|
57
|
+
@sid = sid
|
58
|
+
@secret = secret
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get access auth token for sending pushes
|
62
|
+
#
|
63
|
+
# https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-windows-push-notification-services--wns--overview
|
64
|
+
def get_token
|
65
|
+
body = {
|
66
|
+
grant_type: GRANT_TYPE,
|
67
|
+
client_id: sid,
|
68
|
+
client_secret: secret,
|
69
|
+
scope: SCOPE
|
70
|
+
}
|
71
|
+
|
72
|
+
url = URI.parse ACCESS_TOKEN_URL
|
73
|
+
http = Net::HTTP.new url.host, url.port
|
74
|
+
http.use_ssl = true
|
75
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
76
|
+
response = http.post url.request_uri, URI.encode_www_form(body)
|
77
|
+
|
78
|
+
Response.new response
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
module RubyPushNotifications
|
5
|
+
module WNS
|
6
|
+
# Encapsulates a connection to the WNS service
|
7
|
+
# Responsible for final connection with the service.
|
8
|
+
#
|
9
|
+
class WNSConnection
|
10
|
+
# @private Content-Type HTTP Header type string
|
11
|
+
CONTENT_TYPE_HEADER = 'Content-Type'.freeze
|
12
|
+
|
13
|
+
# @private Content-Length HTTP Header type string
|
14
|
+
CONTENT_LENGTH_HEADER = 'Content-Length'.freeze
|
15
|
+
|
16
|
+
# @private WNS type string
|
17
|
+
X_WNS_TYPE_HEADER = 'X-WNS-Type'.freeze
|
18
|
+
|
19
|
+
# @private Authorization string
|
20
|
+
AUTHORIZATION_HEADER = 'Authorization'.freeze
|
21
|
+
|
22
|
+
# @private Request for status type boolean
|
23
|
+
REQUEST_FOR_STATUS_HEADER = 'X-WNS-RequestForStatus'.freeze
|
24
|
+
|
25
|
+
# @private Content-Type type string
|
26
|
+
CONTENT_TYPE = {
|
27
|
+
badge: 'text/xml',
|
28
|
+
toast: 'text/xml',
|
29
|
+
tile: 'text/xml',
|
30
|
+
raw: 'application/octet-stream'
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
# @private Windows Phone Target Types
|
34
|
+
WP_TARGETS = {
|
35
|
+
badge: 'wns/badge',
|
36
|
+
toast: 'wns/toast',
|
37
|
+
tile: 'wns/tile',
|
38
|
+
raw: 'wns/raw'
|
39
|
+
}.freeze
|
40
|
+
|
41
|
+
# Issues a POST request to the WNS send endpoint to
|
42
|
+
# submit the given notifications.
|
43
|
+
#
|
44
|
+
# @param notifications [WNSNotification]. The notifications object to POST
|
45
|
+
# @param access_token [String] required. Access token for send push
|
46
|
+
# @param options [Hash] optional. Options for GCMPusher. Currently supports:
|
47
|
+
# * open_timeout [Integer]: Number of seconds to wait for the connection to open. Defaults to 30.
|
48
|
+
# * read_timeout [Integer]: Number of seconds to wait for one block to be read. Defaults to 30.
|
49
|
+
# @return [Array]. The response of post
|
50
|
+
# (http://msdn.microsoft.com/pt-br/library/windows/apps/ff941099)
|
51
|
+
def self.post(notifications, access_token, options = {})
|
52
|
+
body = notifications.as_wns_xml
|
53
|
+
headers = build_headers(access_token, notifications.data[:type], body.length.to_s)
|
54
|
+
responses = []
|
55
|
+
notifications.each_device do |url|
|
56
|
+
http = Net::HTTP.new url.host, url.port
|
57
|
+
http.open_timeout = options.fetch(:open_timeout, 30)
|
58
|
+
http.read_timeout = options.fetch(:read_timeout, 30)
|
59
|
+
if url.scheme == 'https'
|
60
|
+
http.use_ssl = true
|
61
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
62
|
+
end
|
63
|
+
response = http.post url.request_uri, body, headers
|
64
|
+
responses << { device_url: url.to_s, headers: capitalize_headers(response), code: response.code.to_i }
|
65
|
+
end
|
66
|
+
WNSResponse.new responses
|
67
|
+
end
|
68
|
+
|
69
|
+
# Build Header based on type and delay
|
70
|
+
#
|
71
|
+
# @param type [String]. Access token
|
72
|
+
# @param type [Symbol]. The type of notification
|
73
|
+
# @param type [String]. Content length of body
|
74
|
+
# @return [Hash]. Correct delay based on notification type
|
75
|
+
# https://msdn.microsoft.com/en-us/library/windows/apps/hh465435.aspx#send_notification_request
|
76
|
+
def self.build_headers(access_token, type, body_length)
|
77
|
+
{
|
78
|
+
CONTENT_TYPE_HEADER => CONTENT_TYPE[type],
|
79
|
+
X_WNS_TYPE_HEADER => WP_TARGETS[type],
|
80
|
+
CONTENT_LENGTH_HEADER => body_length,
|
81
|
+
AUTHORIZATION_HEADER => "Bearer #{access_token}",
|
82
|
+
REQUEST_FOR_STATUS_HEADER => 'true'
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
# Extract headers from response
|
87
|
+
# @param response [Net::HTTPResponse]. HTTP response for request
|
88
|
+
#
|
89
|
+
# @return [Hash]. Hash with headers with case-insensitive keys and capitalized string values
|
90
|
+
def self.capitalize_headers(response)
|
91
|
+
headers = {}
|
92
|
+
response.each_header { |k, v| headers[k] = v.capitalize }
|
93
|
+
headers
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|