fcm-ruby-push-notifications 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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