ruby-push-notifications 1.2.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d47dcca6cd9dfae155c566c9b2b90ba334e5e3f5
4
- data.tar.gz: 957bc60afb47661d910e50f771c46ea108dbe7df
3
+ metadata.gz: 2d72d6fd41a6da51fd7fd7faf2cd56774da3126e
4
+ data.tar.gz: e697f8afde5d62663cf3c1dc3b84599ff770b341
5
5
  SHA512:
6
- metadata.gz: e0c21ac5060259358f3a304934cc562e7d959b7dd3894ce8aaaf87843a49cbf3a5764e450d48f606db1469bafb6f01a423cacabf4d20300e34433f15db550190
7
- data.tar.gz: a012744bfd5eab89450238c778786a84a6bde7df4a7ccd406d0a0fc0bb3f7b53443c8e41708d05b52621b5678b93a136a99d741e8c1a8accaf7b36413969f51b
6
+ metadata.gz: ffd77ae1e4711f547a1a06adab7573e63d9a3cd136a8ead4349d2e2d963b4fb7ad899803d455ce6f8ec34cdd5b79d578ce21c2d8915acd3660759e04ca5ae09c
7
+ data.tar.gz: aa82f2753c87135fba244eb6397a1c85a72827aeb0ca53e21ee8b81067f0a0d2132d3d0cb0f3ac43d89e0ff7a291dde143316a0cd6193eb479db2c32bbf1de16
data/.gitignore CHANGED
@@ -35,3 +35,4 @@ Gemfile.lock
35
35
  .DS_Store
36
36
 
37
37
  examples/*.bak
38
+ .idea/
data/Gemfile CHANGED
@@ -3,4 +3,4 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in ruby-push-notifications.gemspec
4
4
  gemspec
5
5
 
6
- gem "codeclimate-test-reporter", group: :test, require: nil
6
+ gem 'codeclimate-test-reporter', group: :test, require: nil
data/README.md CHANGED
@@ -37,9 +37,10 @@ Or install it yourself as:
37
37
  For completely detailed examples:
38
38
 
39
39
  1. [Apple iOS example](https://github.com/calonso/ruby-push-notifications/tree/master/examples/apns.rb)
40
- 2. [Google Android example](https://github.com/calonso/ruby-push-notifications/tree/master/examples/gcm.rb)
41
- 3. [Windows Phone(MPNS) example](https://github.com/calonso/ruby-push-notifications/tree/master/examples/mpns.rb)
42
- 4. [Windows Phone(WNS) example](https://github.com/calonso/ruby-push-notifications/tree/master/examples/wns.rb)
40
+ 2. [Google Android example (GCM)](https://github.com/calonso/ruby-push-notifications/tree/master/examples/gcm.rb)
41
+ 3. [Google Android example (FCM)](https://github.com/calonso/ruby-push-notifications/tree/master/examples/fcm.rb)
42
+ 4. [Windows Phone(MPNS) example](https://github.com/calonso/ruby-push-notifications/tree/master/examples/mpns.rb)
43
+ 5. [Windows Phone(WNS) example](https://github.com/calonso/ruby-push-notifications/tree/master/examples/wns.rb)
43
44
 
44
45
  ## Pending tasks
45
46
 
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+
5
+ require 'ruby-push-notifications'
6
+
7
+ registration_ids = [
8
+ 'First registration id here',
9
+ 'Second registration id here'
10
+ ]
11
+
12
+ # Example JSON payload
13
+ payload = {
14
+ "notification": {
15
+ "title": 'Greetings Test',
16
+ "body": 'Remember to test! ALOT!',
17
+ "forceStart": 1,
18
+ "sound": 'default',
19
+ "vibrate": 'true',
20
+ "icon": 'fcm_push_icon'
21
+ },
22
+ "android": {
23
+ "priority": 'high',
24
+ "vibrate": 'true'
25
+ },
26
+ "data": {
27
+ "url": 'https://www.google.com'
28
+ },
29
+ "webpush": {
30
+ "headers": {
31
+ "TTL": '60'
32
+ }
33
+ },
34
+ "priority": 'high'
35
+ }
36
+
37
+ notification = RubyPushNotifications::FCM::FCMNotification.new registration_ids, payload
38
+
39
+ pusher = RubyPushNotifications::FCM::FCMPusher.new "Your app's FCM key"
40
+
41
+ pusher.push [notification]
42
+ p 'Notification sending results:'
43
+ p "Success: #{notification.success}, Failed: #{notification.failed}"
44
+ p 'Details:'
45
+ p notification.individual_results
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
  require 'ruby-push-notifications/notification_results_manager'
3
5
  require 'ruby-push-notifications/apns'
4
6
  require 'ruby-push-notifications/gcm'
7
+ require 'ruby-push-notifications/fcm'
5
8
  require 'ruby-push-notifications/mpns'
6
9
  require 'ruby-push-notifications/wns'
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ruby-push-notifications/fcm/fcm_connection'
4
+ require 'ruby-push-notifications/fcm/fcm_notification'
5
+ require 'ruby-push-notifications/fcm/fcm_pusher'
6
+ require 'ruby-push-notifications/fcm/fcm_response'
7
+ require 'ruby-push-notifications/fcm/fcm_error'
8
+ require 'ruby-push-notifications/fcm/fcm_result'
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/https'
5
+
6
+ module RubyPushNotifications
7
+ module FCM
8
+ # Encapsulates a connection to the FCM service
9
+ # Responsible for final connection with the service.
10
+ #
11
+ # @author Carlos Alonso
12
+ class FCMConnection
13
+
14
+ # @private The URL of the Android FCM endpoint
15
+ # Credits: https://github.com/calos0921 - for this url change to FCM std
16
+ FCM_URL = 'https://fcm.googleapis.com/fcm/send'
17
+
18
+ # @private Content-Type HTTP Header string
19
+ CONTENT_TYPE_HEADER = 'Content-Type'
20
+
21
+ # @private Application/JSON content type
22
+ JSON_CONTENT_TYPE = 'application/json'
23
+
24
+ # @private Authorization HTTP Header String
25
+ AUTHORIZATION_HEADER = 'Authorization'
26
+
27
+ # Issues a POST request to the FCM send endpoint to
28
+ # submit the given notifications.
29
+ #
30
+ # @param notification [String]. The text to POST
31
+ # @param key [String]. The FCM sender id to use
32
+ # (https://developer.android.com/google/fcm/fcm.html#senderid)
33
+ # @param options [Hash] optional. Options for #post. Currently supports:
34
+ # * url [String]: URL of the FCM endpoint. Defaults to the official FCM URL.
35
+ # * open_timeout [Integer]: Number of seconds to wait for the connection to open. Defaults to 30.
36
+ # * read_timeout [Integer]: Number of seconds to wait for one block to be read. Defaults to 30.
37
+ # @return [FCMResponse]. The FCMResponse that encapsulates the received response
38
+ def self.post(notification, key, options = {})
39
+ headers = {
40
+ CONTENT_TYPE_HEADER => JSON_CONTENT_TYPE,
41
+ AUTHORIZATION_HEADER => "key=#{key}"
42
+ }
43
+
44
+ url = URI.parse options.fetch(:url, FCM_URL)
45
+ http = Net::HTTP.new url.host, url.port
46
+ http.use_ssl = url.scheme == 'https'
47
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
48
+ http.open_timeout = options.fetch(:open_timeout, 30)
49
+ http.read_timeout = options.fetch(:read_timeout, 30)
50
+
51
+ response = http.post url.path, notification, headers
52
+
53
+ FCMResponse.new response.code.to_i, response.body
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyPushNotifications
4
+ module FCM
5
+ # Base class for all FCM related errors
6
+ #
7
+ # @author Carlos Alonso
8
+ class FCMError < StandardError; end
9
+
10
+ class MalformedFCMJSONError < FCMError; end
11
+
12
+ class FCMAuthError < FCMError; end
13
+
14
+ class FCMInternalError < FCMError; end
15
+
16
+ class UnexpectedFCMResponseError < FCMError; end
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyPushNotifications
4
+ module FCM
5
+ # Encapsulates a FCM Notification.
6
+ # By default only Required fields are set.
7
+ # (https://developer.android.com/google/fcm/server-ref.html#send-downstream)
8
+ #
9
+ # @author Carlos Alonso
10
+ class FCMNotification
11
+ attr_reader :registration_ids, :data
12
+
13
+ include RubyPushNotifications::NotificationResultsManager
14
+
15
+ # Initializes the notification
16
+ #
17
+ # @param [Array]. Array with the receiver's FCM registration ids.
18
+ # @param [Hash]. Payload to send.
19
+ def initialize(registration_ids, data)
20
+ @registration_ids = registration_ids
21
+ @data = data
22
+ end
23
+
24
+ def make_payload
25
+ { registration_ids: @registration_ids }.merge(@data)
26
+ end
27
+
28
+ # @return [String]. The FCM's JSON format for the payload to send.
29
+ # (https://developer.android.com/google/fcm/server-ref.html#send-downstream)
30
+ # Credits: https://github.com/calos0921 - for this url change to FCM std
31
+ def as_fcm_json
32
+ JSON.dump(make_payload)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyPushNotifications
4
+ module FCM
5
+
6
+ # This class is responsible for sending notifications to the FCM service.
7
+ #
8
+ # @author Carlos Alonso
9
+ class FCMPusher
10
+
11
+ # Initializes the FCMPusher
12
+ #
13
+ # @param key [String]. FCM sender id to use
14
+ # ((https://developer.android.com/google/fcm/fcm.html#senderid))
15
+ # @param options [Hash] optional. Options for FCMPusher. Currently supports:
16
+ # * url [String]: URL of the FCM endpoint. Defaults to the official FCM URL.
17
+ # * open_timeout [Integer]: Number of seconds to wait for the connection to open. Defaults to 30.
18
+ # * read_timeout [Integer]: Number of seconds to wait for one block to be read. Defaults to 30.
19
+ def initialize(key, options = {})
20
+ @key = key
21
+ @options = options
22
+ end
23
+
24
+ # Actually pushes the given notifications.
25
+ # Assigns every notification an array with the result of each
26
+ # individual notification.
27
+ #
28
+ # @param notifications [Array]. Array of FCMNotification to send.
29
+ def push(notifications)
30
+ notifications.each do |notif|
31
+ notif.results = FCMConnection.post notif.as_fcm_json, @key, @options
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyPushNotifications
4
+ module FCM
5
+
6
+ # This class encapsulates a response received from the FCM service
7
+ # and helps parsing and understanding the received meesages/codes.
8
+ #
9
+ # @author Carlos Alonso
10
+ class FCMResponse
11
+
12
+ # @return [Integer] the number of successfully sent notifications
13
+ attr_reader :success
14
+
15
+ # @return [Integer] the number of failed notifications
16
+ attr_reader :failed
17
+
18
+ # @return [Integer] the number of received canonical IDS
19
+ # (https://developer.android.com/google/fcm/server-ref.html#table4)
20
+ attr_reader :canonical_ids
21
+
22
+ # @return [Array] Array of a FCMResult for every receiver of the notification
23
+ # sent indicating the result of the operation.
24
+ attr_reader :results
25
+ alias_method :individual_results, :results
26
+
27
+ # Initializes the FCMResponse and runs response parsing
28
+ #
29
+ # @param code [Integer]. The HTTP status code received
30
+ # @param body [String]. The response body received.
31
+ # @raise MalformedFCMJsonError if code == 400 Bad Request
32
+ # @raise FCMAuthError if code == 401 Unauthorized
33
+ # @raise FCMInternalError if code == 5xx
34
+ # @raise UnexpectedFCMResponseError if code != 200
35
+ def initialize(code, body)
36
+ case code
37
+ when 200
38
+ parse_response body
39
+ when 400
40
+ raise MalformedFCMJSONError, body
41
+ when 401
42
+ raise FCMAuthError, body
43
+ when 500..599
44
+ raise FCMInternalError, body
45
+ else
46
+ raise UnexpectedFCMResponseError, code
47
+ end
48
+ end
49
+
50
+ def ==(other)
51
+ (other.is_a?(FCMResponse) &&
52
+ success == other.success &&
53
+ failed == other.failed &&
54
+ canonical_ids == other.canonical_ids &&
55
+ results == other.results) || super(other)
56
+ end
57
+
58
+ private
59
+
60
+ # Parses the response extracting counts for successful, failed and
61
+ # containing canonical ID messages.
62
+ # Also creates the results array assigning a FCMResult subclass for each
63
+ # registration ID the notification was sent to.
64
+ #
65
+ # @param body [String]. The response body
66
+ def parse_response(body)
67
+ json = JSON.parse body, symbolize_names: true
68
+ @success = json[:success]
69
+ @failed = json[:failure]
70
+ @canonical_ids = json[:canonical_ids]
71
+ @results = (json[:results] || []).map { |result| fcm_result_for result }
72
+ end
73
+
74
+ # Factory method that, for each FCM result object assigns a FCMResult
75
+ # subclass.
76
+ #
77
+ # @param result [Hash]. FCM Result parsed hash
78
+ # @return [FCMResult]. Corresponding FCMResult subclass
79
+ def fcm_result_for(result)
80
+ if canonical_id = result[:registration_id]
81
+ FCMCanonicalIDResult.new canonical_id
82
+ elsif error = result[:error]
83
+ FCMResultError.new error
84
+ else
85
+ FCMResultOK.new
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyPushNotifications
4
+ module FCM
5
+ # Class that encapsulates the result of a single sent notification to a single
6
+ # Registration ID
7
+ # (https://developer.android.com/google/fcm/server-ref.html#table4)
8
+ # @author Carlos Alonso
9
+ class FCMResult; end
10
+
11
+ # Indicates that the notification was successfully sent to the corresponding
12
+ # registration ID
13
+ class FCMResultOK < FCMResult
14
+
15
+ def ==(other)
16
+ other.is_a?(FCMResultOK) || super(other)
17
+ end
18
+ end
19
+
20
+ # Indicates that the notification was successfully sent to the corresponding
21
+ # registration ID but FCM sent a canonical ID for it, so the received canonical
22
+ # ID should be used as registration ID ASAP.
23
+ class FCMCanonicalIDResult < FCMResult
24
+
25
+ # @return [String]. The suggested canonical ID from FCM
26
+ attr_reader :canonical_id
27
+
28
+ def initialize(canonical_id)
29
+ @canonical_id = canonical_id
30
+ end
31
+
32
+ def ==(other)
33
+ (other.is_a?(FCMCanonicalIDResult) && @canonical_id == other.canonical_id) ||
34
+ super(other)
35
+ end
36
+ end
37
+
38
+ # An error occurred sending the notification to the registration ID.
39
+ class FCMResultError < FCMResult
40
+
41
+ # @return [String]. The error sent by FCM
42
+ attr_accessor :error
43
+
44
+ def initialize(error)
45
+ @error = error
46
+ end
47
+
48
+ def ==(other)
49
+ (other.is_a?(FCMResultError) && @error == other.error) || super(other)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,9 +1,8 @@
1
1
  # coding: utf-8
2
2
  Gem::Specification.new do |spec|
3
3
  spec.name = "ruby-push-notifications"
4
- spec.version = "1.2.0"
4
+ spec.version = "1.3.0"
5
5
  spec.authors = ['Carlos Alonso']
6
- spec.email = ['info@mrcalonso.com']
7
6
  spec.summary = %q{iOS, Android and Windows Phone Push Notifications made easy!}
8
7
  spec.description = %q{Easy to use gem to send iOS, Android and Windows Phone Push notifications}
9
8
  spec.homepage = 'https://github.com/calonso/ruby-push-notifications'
@@ -19,7 +18,9 @@ Gem::Specification.new do |spec|
19
18
  spec.add_dependency 'builder', '~> 3.0'
20
19
 
21
20
  spec.add_development_dependency 'bundler', '~> 1.6'
21
+ # spec.add_development_dependency 'bundler', '~> 2.0'
22
22
  spec.add_development_dependency 'rake', '~> 10.4'
23
+ spec.add_development_dependency 'simplecov', '~> 0.12.0'
23
24
  spec.add_development_dependency 'rspec', '~> 3.2'
24
25
  spec.add_development_dependency 'factory_girl', '~> 4.0'
25
26
  spec.add_development_dependency 'webmock', '~> 1.20'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  FactoryGirl.define do
2
4
  sequence :apns_token do |i|
3
5
  "ce8be627 2e43e855 16033e24 b4c28922 0eeda487 9c477160 b2545e95 b68b596#{i}"
@@ -7,6 +9,10 @@ FactoryGirl.define do
7
9
  "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ#{i}"
8
10
  end
9
11
 
12
+ sequence :fcm_registration_id do |i|
13
+ "egp-7jEODbM:APA91bGODoU9taMK10FDVvJI_ZhKXHl_GKoLKIDrQ0PTJn5SD2mFOtOiPxeTlkATAYI-eU4oucF6rPsAiXdgu9fFbmuDsprmx_bakCFPvIoN1Axg8SzqtnZpzagc0LZOJxNeZB-VVgcc#{i}"
14
+ end
15
+
10
16
  sequence :mpns_device_url do |i|
11
17
  "http://s.notify.live.net/u/1/bn1/HmQAAACP-0esPuxBSkzBNNXH4W0lV3lK-stEw6eRfpXX39uYbM7IwehXOTO9pRBjaaGECWOdD_7x5j5U4w4iXG4hGxer/d2luZG93c3Bob25lZGVmYXVsdA/EMDhx32Q5BG0DWnZpuVX1g/kRFAu0-jnhMQ-HG94rXzrbb0wQk#{i}"
12
18
  end
@@ -1,26 +1,63 @@
1
+ # frozen_string_literal: true
2
+
1
3
  FactoryGirl.define do
2
- factory :apns_notification, class: 'RubyPushNotifications::APNS::APNSNotification' do
4
+ factory :apns_notification,
5
+ class: 'RubyPushNotifications::APNS::APNSNotification' do
3
6
  tokens { [generate(:apns_token)] }
4
7
  data a: 1
5
8
 
6
9
  initialize_with { new tokens, data }
7
10
  end
8
11
 
9
- factory :gcm_notification, class: 'RubyPushNotifications::GCM::GCMNotification' do
12
+ factory :gcm_notification,
13
+ class: 'RubyPushNotifications::GCM::GCMNotification' do
10
14
  registration_ids { [generate(:gcm_registration_id)] }
11
15
  data a: 1
12
16
 
13
17
  initialize_with { new registration_ids, data }
14
18
  end
15
19
 
16
- factory :mpns_notification, class: 'RubyPushNotifications::MPNS::MPNSNotification' do
20
+ factory :fcm_notification,
21
+ class: 'RubyPushNotifications::FCM::FCMNotification' do
22
+ registration_ids { [generate(:fcm_registration_id)] }
23
+ data a:
24
+ {
25
+ notification: {
26
+ title: 'Greetings Test',
27
+ body: 'Remember to test! ALOT!',
28
+ forceStart: 1,
29
+ sound: 'default',
30
+ vibrate: 'true',
31
+ icon: 'fcm_push_icon'
32
+ },
33
+ android: {
34
+ priority: 'high',
35
+ vibrate: 'true'
36
+ },
37
+ data: {
38
+ url: 'https://www.google.com'
39
+ },
40
+ webpush: {
41
+ headers: {
42
+ TTL: '60'
43
+ }
44
+ },
45
+ priority: 'high'
46
+ }
47
+
48
+ initialize_with { new registration_ids, data }
49
+ end
50
+
51
+ factory :mpns_notification,
52
+ class: 'RubyPushNotifications::MPNS::MPNSNotification' do
17
53
  device_urls { [generate(:mpns_device_url)] }
18
54
  data message: { value1: 'hello' }
19
55
 
20
56
  initialize_with { new device_urls, data }
21
57
  end
22
58
 
23
- factory :wns_notification, class: 'RubyPushNotifications::WNS::WNSNotification' do
59
+ factory :wns_notification,
60
+ class: 'RubyPushNotifications::WNS::WNSNotification' do
24
61
  device_urls { [generate(:wns_device_url)] }
25
62
  data message: { value1: 'hello' }
26
63
 
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyPushNotifications
4
+ module FCM
5
+ describe FCMConnection do
6
+ describe '::post' do
7
+ let(:body) { 'abc' }
8
+ let(:key) { 'def' }
9
+ let(:response) { JSON.dump a: 1 }
10
+
11
+ before do
12
+ stub_request(
13
+ :post,
14
+ 'https://fcm.googleapis.com/fcm/send'
15
+ ).to_return status: [200, 'OK'],
16
+ body: response
17
+ end
18
+ it 'runs the right request' do
19
+ FCMConnection.post body, key
20
+ url = 'https://fcm.googleapis.com/fcm/send'
21
+ headers = { 'Content-Type' => 'application/json',
22
+ 'Authorization' => "key=#{key}" }
23
+
24
+ expect(
25
+ WebMock
26
+ ).to have_requested(:post, url).with(body: body,
27
+ headers: headers).once
28
+ end
29
+
30
+ context 'when :url option is present' do
31
+ it 'posts data to a custom FCM endpoint' do
32
+ test_url = 'https://example.com/fcm/send'
33
+ stub_request(:post, test_url).to_return status: [200, 'OK'],
34
+ body: response
35
+
36
+ FCMConnection.post body, key, url: test_url
37
+ url = 'https://example.com/fcm/send'
38
+ headers = { 'Content-Type' => 'application/json',
39
+ 'Authorization' => "key=#{key}" }
40
+
41
+ expect(
42
+ WebMock
43
+ ).to have_requested(:post,
44
+ url).with(body: body,
45
+ headers: headers).once
46
+ end
47
+ end
48
+
49
+ it 'returns the response encapsulated in a FCMResponse object' do
50
+ expect(FCMConnection.post(body, key)).to eq FCMResponse.new(200,
51
+ response)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyPushNotifications
4
+ module FCM
5
+ describe FCMNotification do
6
+ let(:registration_ids) { %w[a] }
7
+ let(:data) do
8
+ {
9
+ a: {
10
+ notification: {
11
+ title: 'Greetings Test',
12
+ body: 'Remember to test! ALOT!',
13
+ forceStart: 1,
14
+ sound: 'default',
15
+ vibrate: 'true',
16
+ icon: 'fcm_push_icon'
17
+ },
18
+ android: {
19
+ priority: 'high',
20
+ vibrate: 'true'
21
+ },
22
+ data: {
23
+ url: 'https://www.google.com'
24
+ },
25
+ webpush: {
26
+ headers: {
27
+ TTL: '60'
28
+ }
29
+ },
30
+ priority: 'high'
31
+ }
32
+ }
33
+ end
34
+
35
+ let(:notification) do
36
+ build :fcm_notification,
37
+ registration_ids: registration_ids,
38
+ data: data
39
+ end
40
+
41
+ it 'builds the right fcm json' do
42
+ test_case = { registration_ids: registration_ids }.merge(data)
43
+ expect(notification.as_fcm_json).to include(JSON.dump(test_case))
44
+ end
45
+
46
+ it 'validates the registration_ids format'
47
+
48
+ # According to
49
+ # https://developer.android.com/google/fcm/server-ref.html#table1
50
+ it 'validates the data'
51
+
52
+ it_behaves_like 'a proper results manager' do
53
+ let(:success_count) { 2 }
54
+ let(:failures_count) { 1 }
55
+ let(:canonical_id) { 'abcd' }
56
+ let(:individual_results) { [FCMCanonicalIDResult.new(canonical_id)] }
57
+ let(:results) do
58
+ FCMResponse.new 200, JSON.dump(
59
+ success: success_count,
60
+ failure: failures_count,
61
+ results: [
62
+ registration_id: canonical_id
63
+ ]
64
+ )
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyPushNotifications
4
+ module FCM
5
+ describe FCMPusher do
6
+ let(:fcm_key) { 'fcm key' }
7
+ let(:pusher) { FCMPusher.new fcm_key }
8
+
9
+ describe '#push' do
10
+ let(:notif1) { build :fcm_notification }
11
+ let(:notif2) { build :fcm_notification }
12
+ let(:response) { JSON.dump a: 1 }
13
+
14
+ before do
15
+ stub_request(:post, 'https://fcm.googleapis.com/fcm/send').
16
+ to_return status: [200, 'OK'], body: response
17
+ end
18
+
19
+ it 'submits every notification' do
20
+ pusher.push [notif1, notif2]
21
+
22
+ expect(WebMock).
23
+ to have_requested(:post, 'https://fcm.googleapis.com/fcm/send').
24
+ with(body: notif1.as_fcm_json, headers: { 'Content-Type' => 'application/json', 'Authorization' => "key=#{fcm_key}" }).
25
+ once
26
+
27
+ expect(WebMock).
28
+ to have_requested(:post, 'https://fcm.googleapis.com/fcm/send').
29
+ with(body: notif2.as_fcm_json, headers: { 'Content-Type' => 'application/json', 'Authorization' => "key=#{fcm_key}" }).
30
+ once
31
+ end
32
+
33
+ it 'sets results to notifications' do
34
+ expect do
35
+ pusher.push [notif1, notif2]
36
+ end.to change { [notif1, notif2].map &:results }.from([nil, nil]).to [FCMResponse.new(200, response)] * 2
37
+ end
38
+
39
+ it 'splits notifications with more than 1000 destinations in parts'
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyPushNotifications
4
+ module FCM
5
+ describe FCMResponse do
6
+ describe 'success' do
7
+ let(:successful_messages) { 3 }
8
+ let(:failed_messages) { 1 }
9
+ let(:canonical_ids) { 2 }
10
+ let(:body) {
11
+ JSON.dump(
12
+ multicast_id: 123456789,
13
+ success: successful_messages,
14
+ failure: failed_messages,
15
+ canonical_ids: canonical_ids,
16
+ results: [
17
+ { message_id: 1,
18
+ registration_id: 'new_reg_id' },
19
+ { message_id: 2,
20
+ registration_id: 'new_reg_id_2' },
21
+ { message_id: 3 },
22
+ { message_id: 4,
23
+ error: 'NotRegistered' }
24
+ ]
25
+ )
26
+ }
27
+
28
+ let(:response) { FCMResponse.new 200, body }
29
+
30
+ it 'parses the number of successfully processed notifications' do
31
+ expect(response.success).to eq successful_messages
32
+ end
33
+
34
+ it 'parses the number of failed messages' do
35
+ expect(response.failed).to eq failed_messages
36
+ end
37
+
38
+ it 'parses the number of canonical ids received' do
39
+ expect(response.canonical_ids).to eq canonical_ids
40
+ end
41
+
42
+ it 'parses the results' do
43
+ expect(
44
+ response.results
45
+ ).to eq [FCMCanonicalIDResult.new('new_reg_id'),
46
+ FCMCanonicalIDResult.new('new_reg_id_2'),
47
+ FCMResultOK.new,
48
+ FCMResultError.new('NotRegistered')]
49
+ end
50
+ end
51
+
52
+ describe 'errors' do
53
+ let(:dummy_response_body) { 'dummy response body' }
54
+ describe '400 Bad Request error code' do
55
+ it 'raises a MalformedFCMJSONError' do
56
+ expect do
57
+ FCMResponse.new 400, dummy_response_body
58
+ end.to raise_error MalformedFCMJSONError, dummy_response_body
59
+ end
60
+ end
61
+
62
+ describe '401 Unauthorized error code' do
63
+ it 'raises a FCMAuthError' do
64
+ expect do
65
+ FCMResponse.new 401, dummy_response_body
66
+ end.to raise_error FCMAuthError, dummy_response_body
67
+ end
68
+ end
69
+
70
+ describe '500 Internal Server Error error code' do
71
+ it 'raises a FCMInternalError' do
72
+ expect do
73
+ FCMResponse.new 500, dummy_response_body
74
+ end.to raise_error FCMInternalError, dummy_response_body
75
+ end
76
+ end
77
+
78
+ describe '302 Found error code' do
79
+ it 'raises a UnexpectedFCMResponseError' do
80
+ expect do
81
+ FCMResponse.new 302, dummy_response_body
82
+ end.to raise_error UnexpectedFCMResponseError, '302'
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'simplecov'
3
4
  SimpleCov.start
@@ -15,7 +16,7 @@ Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
15
16
  def apns_binary(json, token, id)
16
17
  json = JSON.dump(json) if json.is_a?(Hash)
17
18
  [
18
- 2, 56+json.bytesize, 1, 32, token, 2, json.bytesize, json,
19
+ 2, 56 + json.bytesize, 1, 32, token, 2, json.bytesize, json,
19
20
  3, 4, id, 4, 4, (Time.now + RubyPushNotifications::APNS::APNSNotification::WEEKS_4).to_i, 5, 1, 10
20
21
  ].pack 'cNcnH64cna*cnNcnNcnc'
21
22
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.configure do |config|
2
4
  config.include FactoryGirl::Syntax::Methods
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  shared_examples 'right results' do |success, failed, individual_results|
2
4
  it 'has the expected success value' do
3
5
  expect(notification.success).to eq success
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-push-notifications
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Alonso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-20 00:00:00.000000000 Z
11
+ date: 2019-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: builder
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '10.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.12.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.12.0
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -95,8 +109,7 @@ dependencies:
95
109
  - !ruby/object:Gem::Version
96
110
  version: '1.20'
97
111
  description: Easy to use gem to send iOS, Android and Windows Phone Push notifications
98
- email:
99
- - info@mrcalonso.com
112
+ email:
100
113
  executables: []
101
114
  extensions: []
102
115
  extra_rdoc_files: []
@@ -110,6 +123,7 @@ files:
110
123
  - README.md
111
124
  - Rakefile
112
125
  - examples/apns.rb
126
+ - examples/fcm.rb
113
127
  - examples/gcm.rb
114
128
  - examples/mpns.rb
115
129
  - examples/wns.rb
@@ -120,6 +134,13 @@ files:
120
134
  - lib/ruby-push-notifications/apns/apns_notification.rb
121
135
  - lib/ruby-push-notifications/apns/apns_pusher.rb
122
136
  - lib/ruby-push-notifications/apns/apns_results.rb
137
+ - lib/ruby-push-notifications/fcm.rb
138
+ - lib/ruby-push-notifications/fcm/fcm_connection.rb
139
+ - lib/ruby-push-notifications/fcm/fcm_error.rb
140
+ - lib/ruby-push-notifications/fcm/fcm_notification.rb
141
+ - lib/ruby-push-notifications/fcm/fcm_pusher.rb
142
+ - lib/ruby-push-notifications/fcm/fcm_response.rb
143
+ - lib/ruby-push-notifications/fcm/fcm_result.rb
123
144
  - lib/ruby-push-notifications/gcm.rb
124
145
  - lib/ruby-push-notifications/gcm/gcm_connection.rb
125
146
  - lib/ruby-push-notifications/gcm/gcm_error.rb
@@ -147,6 +168,10 @@ files:
147
168
  - spec/ruby-push-notifications/apns/apns_connection_spec.rb
148
169
  - spec/ruby-push-notifications/apns/apns_notification_spec.rb
149
170
  - spec/ruby-push-notifications/apns/apns_pusher_spec.rb
171
+ - spec/ruby-push-notifications/fcm/fcm_connection_spec.rb
172
+ - spec/ruby-push-notifications/fcm/fcm_notification_spec.rb
173
+ - spec/ruby-push-notifications/fcm/fcm_pusher_spec.rb
174
+ - spec/ruby-push-notifications/fcm/fcm_response_spec.rb
150
175
  - spec/ruby-push-notifications/gcm/gcm_connection_spec.rb
151
176
  - spec/ruby-push-notifications/gcm/gcm_notification_spec.rb
152
177
  - spec/ruby-push-notifications/gcm/gcm_pusher_spec.rb
@@ -184,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
184
209
  version: '0'
185
210
  requirements: []
186
211
  rubyforge_project:
187
- rubygems_version: 2.6.11
212
+ rubygems_version: 2.5.2.3
188
213
  signing_key:
189
214
  specification_version: 4
190
215
  summary: iOS, Android and Windows Phone Push Notifications made easy!
@@ -194,6 +219,10 @@ test_files:
194
219
  - spec/ruby-push-notifications/apns/apns_connection_spec.rb
195
220
  - spec/ruby-push-notifications/apns/apns_notification_spec.rb
196
221
  - spec/ruby-push-notifications/apns/apns_pusher_spec.rb
222
+ - spec/ruby-push-notifications/fcm/fcm_connection_spec.rb
223
+ - spec/ruby-push-notifications/fcm/fcm_notification_spec.rb
224
+ - spec/ruby-push-notifications/fcm/fcm_pusher_spec.rb
225
+ - spec/ruby-push-notifications/fcm/fcm_response_spec.rb
197
226
  - spec/ruby-push-notifications/gcm/gcm_connection_spec.rb
198
227
  - spec/ruby-push-notifications/gcm/gcm_notification_spec.rb
199
228
  - spec/ruby-push-notifications/gcm/gcm_pusher_spec.rb