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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +37 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +19 -0
  5. data/CHANGELOG.md +32 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE +22 -0
  8. data/README.md +82 -0
  9. data/Rakefile +12 -0
  10. data/examples/apns.rb +27 -0
  11. data/examples/gcm.rb +25 -0
  12. data/examples/mpns.rb +22 -0
  13. data/examples/wns.rb +25 -0
  14. data/gemfiles/Gemfile-legacy +8 -0
  15. data/lib/ruby-push-notifications.rb +7 -0
  16. data/lib/ruby-push-notifications/apns.rb +44 -0
  17. data/lib/ruby-push-notifications/apns/apns_connection.rb +70 -0
  18. data/lib/ruby-push-notifications/apns/apns_notification.rb +91 -0
  19. data/lib/ruby-push-notifications/apns/apns_pusher.rb +84 -0
  20. data/lib/ruby-push-notifications/apns/apns_results.rb +30 -0
  21. data/lib/ruby-push-notifications/fcm.rb +8 -0
  22. data/lib/ruby-push-notifications/fcm/fcm_connection.rb +60 -0
  23. data/lib/ruby-push-notifications/fcm/fcm_error.rb +17 -0
  24. data/lib/ruby-push-notifications/fcm/fcm_notification.rb +31 -0
  25. data/lib/ruby-push-notifications/fcm/fcm_pusher.rb +29 -0
  26. data/lib/ruby-push-notifications/fcm/fcm_request.rb +28 -0
  27. data/lib/ruby-push-notifications/fcm/fcm_response.rb +89 -0
  28. data/lib/ruby-push-notifications/fcm/fcm_result.rb +52 -0
  29. data/lib/ruby-push-notifications/gcm.rb +6 -0
  30. data/lib/ruby-push-notifications/gcm/gcm_connection.rb +54 -0
  31. data/lib/ruby-push-notifications/gcm/gcm_error.rb +17 -0
  32. data/lib/ruby-push-notifications/gcm/gcm_notification.rb +31 -0
  33. data/lib/ruby-push-notifications/gcm/gcm_pusher.rb +35 -0
  34. data/lib/ruby-push-notifications/gcm/gcm_response.rb +89 -0
  35. data/lib/ruby-push-notifications/gcm/gcm_result.rb +52 -0
  36. data/lib/ruby-push-notifications/mpns.rb +5 -0
  37. data/lib/ruby-push-notifications/mpns/mpns_connection.rb +87 -0
  38. data/lib/ruby-push-notifications/mpns/mpns_notification.rb +94 -0
  39. data/lib/ruby-push-notifications/mpns/mpns_pusher.rb +33 -0
  40. data/lib/ruby-push-notifications/mpns/mpns_response.rb +82 -0
  41. data/lib/ruby-push-notifications/mpns/mpns_result.rb +182 -0
  42. data/lib/ruby-push-notifications/notification_results_manager.rb +14 -0
  43. data/lib/ruby-push-notifications/wns.rb +6 -0
  44. data/lib/ruby-push-notifications/wns/wns_access.rb +82 -0
  45. data/lib/ruby-push-notifications/wns/wns_connection.rb +97 -0
  46. data/lib/ruby-push-notifications/wns/wns_notification.rb +101 -0
  47. data/lib/ruby-push-notifications/wns/wns_pusher.rb +32 -0
  48. data/lib/ruby-push-notifications/wns/wns_response.rb +83 -0
  49. data/lib/ruby-push-notifications/wns/wns_result.rb +208 -0
  50. data/ruby-push-notifications.gemspec +28 -0
  51. data/spec/factories.rb +17 -0
  52. data/spec/factories/notifications.rb +30 -0
  53. data/spec/ruby-push-notifications/apns/apns_connection_spec.rb +92 -0
  54. data/spec/ruby-push-notifications/apns/apns_notification_spec.rb +42 -0
  55. data/spec/ruby-push-notifications/apns/apns_pusher_spec.rb +295 -0
  56. data/spec/ruby-push-notifications/gcm/gcm_connection_spec.rb +46 -0
  57. data/spec/ruby-push-notifications/gcm/gcm_notification_spec.rb +37 -0
  58. data/spec/ruby-push-notifications/gcm/gcm_pusher_spec.rb +45 -0
  59. data/spec/ruby-push-notifications/gcm/gcm_response_spec.rb +82 -0
  60. data/spec/ruby-push-notifications/mpns/mpns_connection_spec.rb +46 -0
  61. data/spec/ruby-push-notifications/mpns/mpns_notification_spec.rb +53 -0
  62. data/spec/ruby-push-notifications/mpns/mpns_pusher_spec.rb +59 -0
  63. data/spec/ruby-push-notifications/mpns/mpns_response_spec.rb +64 -0
  64. data/spec/ruby-push-notifications/wns/wns_access_spec.rb +76 -0
  65. data/spec/ruby-push-notifications/wns/wns_connection_spec.rb +53 -0
  66. data/spec/ruby-push-notifications/wns/wns_notification_spec.rb +177 -0
  67. data/spec/ruby-push-notifications/wns/wns_pusher_spec.rb +58 -0
  68. data/spec/ruby-push-notifications/wns/wns_response_spec.rb +65 -0
  69. data/spec/spec_helper.rb +23 -0
  70. data/spec/support/dummy.pem +44 -0
  71. data/spec/support/factory_girl.rb +5 -0
  72. data/spec/support/results_shared_examples.rb +31 -0
  73. metadata +249 -0
@@ -0,0 +1,101 @@
1
+ require 'builder'
2
+
3
+ module RubyPushNotifications
4
+ module WNS
5
+ # Encapsulates a WNS Notification.
6
+ # Actually support for raw, toast, tiles notifications
7
+ # (http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202945)
8
+ #
9
+ class WNSNotification
10
+ include RubyPushNotifications::NotificationResultsManager
11
+
12
+ # @return [Hash]. Payload to send.
13
+ # Toast :title => a bold message
14
+ # :message => the small message
15
+ # :param => a string parameter that is passed to the app
16
+ # Tile :image => a new image for the tile
17
+ # :count => a number to show on the tile
18
+ # :title => the new title of the tile
19
+ # :back_image => an image for the back of the tile
20
+ # :back_title => a title on the back of the tile
21
+ # :back_content => some content (text) for the back
22
+ # Raw :message => the full Hash message body
23
+ attr_reader :data
24
+
25
+ # @return [Array]. Array with the receiver's WNS device URLs.
26
+ attr_reader :device_urls
27
+
28
+ # Initializes the notification
29
+ #
30
+ # @param [Array]. Array with the receiver's device urls.
31
+ # @param [Hash]. Payload to send.
32
+ # Toast :title => a bold message
33
+ # :message => the small message
34
+ # :param => a string parameter that is passed to the app
35
+ # Tile :image => a new image for the tile
36
+ # :count => a number to show on the tile
37
+ # :title => the new title of the tile
38
+ # :back_image => an image for the back of the tile
39
+ # :back_title => a title on the back of the tile
40
+ # :back_content => some content (text) for the back
41
+ # Raw :message => the full XML message body
42
+ def initialize(device_urls, data)
43
+ @device_urls = device_urls
44
+ @data = data
45
+ @data[:type] ||= :raw
46
+ end
47
+
48
+ # @return [String]. The WNS's XML format for the payload to send.
49
+ # (https://docs.microsoft.com/en-us/uwp/schemas/tiles/tiles-xml-schema-portal)
50
+ def as_wns_xml
51
+ xml = Builder::XmlMarkup.new
52
+ xml.instruct!
53
+ if data[:type] != :raw
54
+ case data[:type]
55
+ when :toast
56
+ xml.tag!('toast', **launch_params(data)) do
57
+ xml.tag!('visual') do
58
+ xml.tag!('binding', template: data[:template] || 'ToastText02') do
59
+ xml.tag!('text', id: 1) { xml.text!(data[:title].to_s) }
60
+ xml.tag!('text', id: 2) { xml.text!(data[:message].to_s) }
61
+ end
62
+ end
63
+ end
64
+ when :tile
65
+ xml.tag!('tile') do
66
+ xml.tag!('visual') do
67
+ xml.tag!('binding', template: data[:template] || 'TileWideImageAndText01') do
68
+ xml.tag!('image', src: data[:image].to_s)
69
+ xml.tag!('text') { xml.text!(data[:message].to_s) }
70
+ end
71
+ end
72
+ end
73
+ when :badge
74
+ xml.tag!('badge', value: data[:value])
75
+ end
76
+ else
77
+ xml.root { build_hash(xml, data[:message]) }
78
+ end
79
+ xml.target!
80
+ end
81
+
82
+ def each_device
83
+ @device_urls.each do |url|
84
+ yield(URI.parse url)
85
+ end
86
+ end
87
+
88
+ def build_hash(xml, options)
89
+ return unless options
90
+ options.each do |k, v|
91
+ xml.tag!(k.to_s) { v.is_a?(Hash) ? build_hash(xml, v) : xml.text!(v.to_s) }
92
+ end
93
+ end
94
+
95
+ def launch_params(data)
96
+ return {} unless data[:param]
97
+ { launch: data[:param].to_json }
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,32 @@
1
+ module RubyPushNotifications
2
+ module WNS
3
+
4
+ # This class is responsible for sending notifications to the WNS service.
5
+ #
6
+ class WNSPusher
7
+
8
+ # Initializes the WNSPusher
9
+ #
10
+ # @param access_token [String]. WNS access token.
11
+ # @param options [Hash] optional. Options for GCMPusher. Currently supports:
12
+ # * open_timeout [Integer]: Number of seconds to wait for the connection to open. Defaults to 30.
13
+ # * read_timeout [Integer]: Number of seconds to wait for one block to be read. Defaults to 30.
14
+ # (http://msdn.microsoft.com/pt-br/library/windows/apps/ff941099)
15
+ def initialize(access_token, options = {})
16
+ @access_token = access_token
17
+ @options = options
18
+ end
19
+
20
+ # Actually pushes the given notifications.
21
+ # Assigns every notification an array with the result of each
22
+ # individual notification.
23
+ #
24
+ # @param notifications [Array]. Array of WNSNotification to send.
25
+ def push(notifications)
26
+ notifications.each do |notif|
27
+ notif.results = WNSConnection.post notif, @access_token, @options
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,83 @@
1
+ module RubyPushNotifications
2
+ module WNS
3
+
4
+ # This class encapsulates a response received from the WNS service
5
+ # and helps parsing and understanding the received messages/codes.
6
+ #
7
+ class WNSResponse
8
+
9
+ # @return [Integer] the number of successfully sent notifications
10
+ attr_reader :success
11
+
12
+ # @return [Integer] the number of failed notifications
13
+ attr_reader :failed
14
+
15
+ # @return [Array] Array of a WNSResult for every receiver of the notification
16
+ # sent indicating the result of the operation.
17
+ attr_reader :results
18
+ alias_method :individual_results, :results
19
+
20
+ # Initializes the WNSResponse and runs response parsing
21
+ #
22
+ # @param responses [Array]. Array with device_urls and http responses
23
+ def initialize(responses)
24
+ parse_response responses
25
+ end
26
+
27
+ def ==(other)
28
+ (other.is_a?(WNSResponse) &&
29
+ success == other.success &&
30
+ failed == other.failed &&
31
+ results == other.results) || super(other)
32
+ end
33
+
34
+ private
35
+
36
+ # Parses the response extracting counts for successful, failed messages.
37
+ # Also creates the results array assigning a WNSResult subclass for each
38
+ # device URL the notification was sent to.
39
+ #
40
+ # @param responses [Array]. Array of hash responses
41
+ def parse_response(responses)
42
+ @success = responses.count { |response| response[:code] == 200 }
43
+ @failed = responses.count { |response| response[:code] != 200 }
44
+ @results = responses.map do |response|
45
+ wns_result_for response[:code],
46
+ response[:device_url],
47
+ response[:headers]
48
+ end
49
+ end
50
+
51
+ # Factory method that, for each WNS result object assigns a WNSResult
52
+ # subclass.
53
+ #
54
+ # @param code [Integer]. The HTTP status code received
55
+ # @param device_url [String]. The receiver's WNS device url.
56
+ # @param headers [Hash]. The HTTP headers received.
57
+ # @return [WNSResult]. Corresponding WNSResult subclass
58
+ def wns_result_for(code, device_url, headers)
59
+ case code
60
+ when 200
61
+ WNSResultOK.new device_url, headers
62
+ when 400
63
+ MalformedWNSResultError.new device_url
64
+ when 401
65
+ WNSAuthError.new device_url
66
+ when 404
67
+ WNSInvalidError.new device_url, headers
68
+ when 406
69
+ WNSLimitError.new device_url, headers
70
+ when 410
71
+ WNSExpiredError.new device_url, headers
72
+ when 412
73
+ WNSPreConditionError.new device_url, headers
74
+ when 500..599
75
+ WNSInternalError.new device_url
76
+ else
77
+ WNSResultError.new device_url
78
+ end
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,208 @@
1
+ module RubyPushNotifications
2
+ module WNS
3
+ # Class that encapsulates the result of a single sent notification to a single
4
+ # Device URL
5
+ # https://msdn.microsoft.com/en-us/library/windows/apps/hh465435.aspx#WNSResponseCodes
6
+ class WNSResult
7
+ # @return [String]. Receiver WNS device URL.
8
+ attr_accessor :device_url
9
+
10
+ # @private X-WNS-NotificationStatus HTTP Header string
11
+ X_NOTIFICATION_STATUS = 'x-wns-notificationstatus'
12
+
13
+ # @private X-WNS-DeviceConnectionStatus HTTP Header string
14
+ X_DEVICE_CONNECTION_STATUS = 'x-wns-deviceconnectionstatus'
15
+
16
+ # @private X-SubscriptionStatus HTTP Header string
17
+ X_STATUS = 'x-wns-status'
18
+ end
19
+
20
+ # Indicates that the notification was successfully sent to the corresponding
21
+ # device URL
22
+ class WNSResultOK < WNSResult
23
+ # @return [String]. The status of the notification received
24
+ # by the Windows Notification Service.
25
+ attr_accessor :notification_status
26
+
27
+ # @return [String]. The connection status of the device.
28
+ attr_accessor :device_connection_status
29
+
30
+ # @return [String]. Notification status.
31
+ attr_accessor :status
32
+
33
+ def initialize(device_url, headers)
34
+ @device_url = device_url
35
+ @notification_status = headers[X_NOTIFICATION_STATUS]
36
+ @device_connection_status = headers[X_DEVICE_CONNECTION_STATUS]
37
+ @status = headers[X_STATUS]
38
+ end
39
+
40
+ def ==(other)
41
+ (other.is_a?(WNSResultOK) &&
42
+ device_url == other.device_url &&
43
+ notification_status == other.notification_status &&
44
+ device_connection_status == other.device_connection_status &&
45
+ status == other.status) || super(other)
46
+ end
47
+ end
48
+
49
+ # This error occurs when the cloud service sends a notification
50
+ # request with a bad XML document or malformed notification URI.
51
+ class MalformedWNSResultError < WNSResult
52
+ def initialize(device_url)
53
+ @device_url = device_url
54
+ end
55
+
56
+ def ==(other)
57
+ (other.is_a?(MalformedWNSResultError) &&
58
+ device_url == other.device_url) || super(other)
59
+ end
60
+ end
61
+
62
+ # Sending this notification is unauthorized.
63
+ class WNSAuthError < WNSResult
64
+ def initialize(device_url)
65
+ @device_url = device_url
66
+ end
67
+
68
+ def ==(other)
69
+ (other.is_a?(WNSAuthError) &&
70
+ device_url == other.device_url) || super(other)
71
+ end
72
+ end
73
+
74
+ # Notification is invalid and is not present on the Push Notification Service.
75
+ class WNSInvalidError < WNSResult
76
+ # @return [String]. The status of the notification received
77
+ # by the Windows Notification Service.
78
+ attr_accessor :notification_status
79
+
80
+ # @return [String]. The connection status of the device.
81
+ attr_accessor :device_connection_status
82
+
83
+ # @return [String]. Notification status.
84
+ attr_accessor :status
85
+
86
+ def initialize(device_url, headers)
87
+ @device_url = device_url
88
+ @notification_status = headers[X_NOTIFICATION_STATUS]
89
+ @device_connection_status = headers[X_DEVICE_CONNECTION_STATUS]
90
+ @status = headers[X_STATUS]
91
+ end
92
+
93
+ def ==(other)
94
+ (other.is_a?(WNSInvalidError) &&
95
+ device_url == other.device_url &&
96
+ notification_status == other.notification_status &&
97
+ device_connection_status == other.device_connection_status &&
98
+ status == other.status) || super(other)
99
+ end
100
+ end
101
+
102
+ # This error occurs when an unauthenticated cloud service has reached
103
+ # the per-day throttling limit for a subscription,
104
+ # or when a cloud service (authenticated or unauthenticated)
105
+ # has sent too many notifications per second.
106
+ class WNSLimitError < WNSResult
107
+ # @return [String]. The status of the notification received
108
+ # by the Windows Notification Service.
109
+ attr_accessor :notification_status
110
+
111
+ # @return [String]. The connection status of the device.
112
+ attr_accessor :device_connection_status
113
+
114
+ # @return [String]. Notification status.
115
+ attr_accessor :status
116
+
117
+ def initialize(device_url, headers)
118
+ @device_url = device_url
119
+ @notification_status = headers[X_NOTIFICATION_STATUS]
120
+ @device_connection_status = headers[X_DEVICE_CONNECTION_STATUS]
121
+ @status = headers[X_STATUS]
122
+ end
123
+
124
+ def ==(other)
125
+ (other.is_a?(WNSLimitError) &&
126
+ device_url == other.device_url &&
127
+ notification_status == other.notification_status &&
128
+ device_connection_status == other.device_connection_status &&
129
+ status == other.status) || super(other)
130
+ end
131
+ end
132
+
133
+ # The device is in a disconnected state.
134
+ class WNSPreConditionError < WNSResult
135
+ # @return [String]. The status of the notification received
136
+ # by the Windows Notification Service.
137
+ attr_accessor :notification_status
138
+
139
+ # @return [String]. The connection status of the device.
140
+ attr_accessor :device_connection_status
141
+
142
+ def initialize(device_url, headers)
143
+ @device_url = device_url
144
+ @notification_status = headers[X_NOTIFICATION_STATUS]
145
+ @device_connection_status = headers[X_DEVICE_CONNECTION_STATUS]
146
+ end
147
+
148
+ def ==(other)
149
+ (other.is_a?(WNSPreConditionError) &&
150
+ device_url == other.device_url &&
151
+ notification_status == other.notification_status &&
152
+ device_connection_status == other.device_connection_status) || super(other)
153
+ end
154
+ end
155
+
156
+ # The device is in a disconnected state.
157
+ class WNSExpiredError < WNSResult
158
+ # @return [String]. The status of the notification received
159
+ # by the Windows Notification Service.
160
+ attr_accessor :notification_status
161
+
162
+ # @return [String]. The connection status of the device.
163
+ attr_accessor :device_connection_status
164
+
165
+ # @return [String]. Notification status.
166
+ attr_accessor :status
167
+
168
+ def initialize(device_url, headers)
169
+ @device_url = device_url
170
+ @notification_status = headers[X_NOTIFICATION_STATUS]
171
+ @device_connection_status = headers[X_DEVICE_CONNECTION_STATUS]
172
+ @status = 'Expired'.freeze
173
+ end
174
+
175
+ def ==(other)
176
+ (other.is_a?(WNSExpiredError) &&
177
+ device_url == other.device_url &&
178
+ notification_status == other.notification_status &&
179
+ device_connection_status == other.device_connection_status &&
180
+ status == other.status) || super(other)
181
+ end
182
+ end
183
+
184
+ # The Push Notification Service is unable to process the request.
185
+ class WNSInternalError < WNSResult
186
+ def initialize(device_url)
187
+ @device_url = device_url
188
+ end
189
+
190
+ def ==(other)
191
+ (other.is_a?(WNSInternalError) &&
192
+ device_url == other.device_url) || super(other)
193
+ end
194
+ end
195
+
196
+ # Unknow Error
197
+ class WNSResultError < WNSResult
198
+ def initialize(device_url)
199
+ @device_url = device_url
200
+ end
201
+
202
+ def ==(other)
203
+ (other.is_a?(WNSResultError) &&
204
+ device_url == other.device_url) || super(other)
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ Gem::Specification.new do |spec|
3
+ spec.name = "fcm-ruby-push-notifications"
4
+ spec.version = "1.2.0"
5
+ spec.authors = ['Santhu MS']
6
+ spec.email = ['santhu.ms83@gmail.com']
7
+ spec.summary = %q{iOS, Android and Windows Phone Push Notifications made easy!}
8
+ spec.description = %q{Easy to use gem to send iOS, Android and Windows Phone Push notifications}
9
+ spec.homepage = 'https://github.com/santhums/ruby-push-notifications'
10
+ spec.license = 'MIT'
11
+
12
+ spec.required_ruby_version = '>= 2.0.0'
13
+
14
+ spec.files = `git ls-files -z`.split("\x0")
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ['lib']
18
+
19
+ spec.add_dependency 'builder', '~> 3.0'
20
+ spec.add_dependency 'httparty'
21
+ spec.add_dependency 'json'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.6'
24
+ spec.add_development_dependency 'rake', '~> 10.4'
25
+ spec.add_development_dependency 'rspec', '~> 3.2'
26
+ spec.add_development_dependency 'factory_girl', '~> 4.0'
27
+ spec.add_development_dependency 'webmock', '~> 1.20'
28
+ end