ruby-push-notifications 1.2.0 → 1.3.0

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