mercurius 0.1.5 → 0.1.6

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: c9d092e2bac4a62f415675ab2377d5bbef233fda
4
- data.tar.gz: 39e988fb569e75e8053352d00fee748cfa6b9028
3
+ metadata.gz: 2ab94d197c802c11b53e6fc8dc34090237b456af
4
+ data.tar.gz: 4dbcf381ccc7185f1cb27514ac24ff3d80bf8332
5
5
  SHA512:
6
- metadata.gz: fbe5d38205bde19ef187af2287c029a30a2d348e874b10c4da3b24c9dd77a92bfb04edf58ee5854e38e7f8d1aa90bb3b87b4c47cdd89789d769c45103ec68fe2
7
- data.tar.gz: a868cdc0297e17c69e72fbec34c4d615e4570d3d59169de4acb89d59f2230d8c9f7a73ee6ee802e9a83a16cb1b3129a6c7584552a279888ce32d77bed641b297
6
+ metadata.gz: 098bd1d77a6e27e53e52cc873aeee6b8428569d47fbf855af51cd2624ab0470c7cfede9e0dc8b1e092100e779d78c6d0843751bacbba837e332102b2351e77f7
7
+ data.tar.gz: fee9b5c35b361e65a8d4496e9127f5627b2078ab249349477b9827fe24855125c0664bf66fc5946735d2a7fc7539eb87d9856cfd3d8f8904724c5165b08f21a3
data/README.md CHANGED
@@ -28,14 +28,14 @@ Create the service with:
28
28
 
29
29
  Now create the notification that you wish to send:
30
30
 
31
- gcm_notification = GCM::Notification.new(alert: 'Hey')
31
+ gcm_notification = GCM::Notification.new(data: { alert: 'Hey' })
32
32
 
33
33
  You can deliver the gcm_notification in the following manners:
34
34
 
35
- gcm_service.deliver gcm_notification, 'token123' # single recipient
36
- gcm_service.deliver gcm_notification, 'token123', 'token456' # multiple recipients
37
- token_array = ['token123', 'token456']
38
- gcm_service.deliver gcm_notification, token_array # multiple recipients
35
+ gcm_service.deliver gcm_notification, 'token123' # single recipient
36
+ gcm_service.deliver gcm_notification, 'token123', 'token456' # multiple recipients
37
+ gcm_service.deliver gcm_notification, ['token123', 'token456'] # multiple recipients
38
+ gcm_service.deliver gcm_notification, topic: 'topic123' # topic delivery
39
39
 
40
40
  ## APNS
41
41
 
data/bin/rake ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rake', 'rake')
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
data/lib/mercurius.rb CHANGED
@@ -23,4 +23,6 @@ require 'mercurius/gcm/connection'
23
23
  require 'mercurius/gcm/notification'
24
24
  require 'mercurius/gcm/service'
25
25
  require 'mercurius/gcm/response'
26
+ require 'mercurius/gcm/response_collection'
27
+ require 'mercurius/gcm/result_collection'
26
28
  require 'mercurius/gcm/result'
@@ -34,13 +34,11 @@ module APNS
34
34
  end
35
35
 
36
36
  private
37
-
38
37
  def ssl_context_for_pem(pem)
39
38
  context = OpenSSL::SSL::SSLContext.new
40
39
  context.cert = OpenSSL::X509::Certificate.new(pem.data)
41
40
  context.key = OpenSSL::PKey::RSA.new(pem.data, pem.password)
42
41
  context
43
42
  end
44
-
45
43
  end
46
44
  end
@@ -26,16 +26,6 @@ module APNS
26
26
  [0, 0, 32, package_device_token(device_token), 0, packaged_message.bytesize, packaged_message].pack("ccca*cca*")
27
27
  end
28
28
 
29
- def package_device_token(device_token)
30
- [device_token.gsub(/[\s|<|>]/,'')].pack('H*')
31
- end
32
-
33
- def packaged_message
34
- { aps: payload }.to_json.gsub(/\\u([\da-fA-F]{4})/) do |m|
35
- [$1].pack("H*").unpack("n*").pack("U*")
36
- end
37
- end
38
-
39
29
  def ==(that)
40
30
  attributes == that.attributes
41
31
  end
@@ -44,5 +34,15 @@ module APNS
44
34
  packaged_message.bytesize <= MAX_PAYLOAD_BYTES
45
35
  end
46
36
 
37
+ private
38
+ def package_device_token(device_token)
39
+ [device_token.gsub(/[\s|<|>]/,'')].pack('H*')
40
+ end
41
+
42
+ def packaged_message
43
+ { aps: payload }.to_json.gsub(/\\u([\da-fA-F]{4})/) do |m|
44
+ [$1].pack("H*").unpack("n*").pack("U*")
45
+ end
46
+ end
47
47
  end
48
48
  end
@@ -9,7 +9,6 @@ module APNS
9
9
  end
10
10
 
11
11
  private
12
-
13
12
  def read_file_at_path
14
13
  if File.exist? path
15
14
  File.read path
@@ -17,6 +16,5 @@ module APNS
17
16
  raise PemNotFoundError.new
18
17
  end
19
18
  end
20
-
21
19
  end
22
20
  end
@@ -33,7 +33,6 @@ module APNS
33
33
  end
34
34
 
35
35
  private
36
-
37
36
  def persistent?
38
37
  @_persistent
39
38
  end
@@ -57,6 +56,5 @@ module APNS
57
56
  def too_many_retries?
58
57
  @attempts >= MAX_NUMBER_OF_RETRIES
59
58
  end
60
-
61
59
  end
62
60
  end
@@ -1,7 +1,5 @@
1
1
  class InvalidApnsModeError < StandardError
2
-
3
2
  def initialize
4
3
  super 'Tried to set invalid APNS mode.'
5
4
  end
6
-
7
5
  end
@@ -1,7 +1,5 @@
1
1
  class PemNotConfiguredError < StandardError
2
-
3
2
  def initialize
4
3
  super 'PEM is not configured properly.'
5
4
  end
6
-
7
- end
5
+ end
@@ -1,7 +1,5 @@
1
1
  class PemNotFoundError < StandardError
2
-
3
2
  def initialize
4
3
  super 'The specified PEM file does not exist.'
5
4
  end
6
-
7
- end
5
+ end
@@ -1,7 +1,5 @@
1
1
  class TooManyRetriesError < StandardError
2
-
3
2
  def initialize(e)
4
3
  super "APNS Service has reached it's maximum number of retries. Error: #{e.message}"
5
4
  end
6
-
7
5
  end
@@ -1,36 +1,22 @@
1
1
  module GCM
2
2
  class Notification
3
3
  include ActiveModel::Model
4
-
5
- def self.special_attrs
6
- [:collapse_key, :time_to_live, :delay_while_idle, :dry_run, :notification]
7
- end
8
-
9
- attr_accessor :data
10
- attr_accessor *special_attrs
11
- # validate delay_while_idle is true/false
12
- # validate ttl is integer in seconds
4
+ attr_accessor :attributes
13
5
 
14
6
  def initialize(attributes = {})
15
7
  @attributes = attributes
16
- super @attributes.slice(*self.class.special_attrs).merge(data: @attributes.except(*self.class.special_attrs))
17
8
  end
18
9
 
19
10
  def to_h
20
- hash = {
21
- notification: notification,
22
- data: data,
23
- collapse_key: collapse_key,
24
- time_to_live: time_to_live,
25
- delay_while_idle: delay_while_idle,
26
- dry_run: dry_run
27
- }
28
-
29
- hash.reject { |k, v| v.nil? }
11
+ @attributes
30
12
  end
31
13
 
32
14
  def ==(that)
33
15
  attributes == that.attributes
34
16
  end
17
+
18
+ def method_missing(method, *args, &block)
19
+ @attributes.fetch(method) { super }
20
+ end
35
21
  end
36
22
  end
@@ -1,49 +1,55 @@
1
1
  module GCM
2
2
  class Response
3
- attr_reader :response, :device_tokens
4
-
5
- MESSAGES = {
6
- 200 => 'Success',
7
- 400 => 'The request could not be parsed as JSON or it contained invalid fields',
8
- 401 => 'There was an error authenticating the sender account',
9
- 500 => 'There was an internal error in the GCM server',
10
- 503 => 'GCM server is temporarily unavailable',
11
- default: 'Unknown error'
12
- }
13
-
14
- def initialize(response, device_tokens)
3
+ attr_reader :response, :tokens
4
+
5
+ def initialize(response, tokens = [])
15
6
  @response = response
16
- @device_tokens = device_tokens
7
+ @tokens = tokens
17
8
  end
18
9
 
19
10
  def status
20
- @response.status
21
- end
22
-
23
- def message
24
- MESSAGES.fetch(status, MESSAGES[:default])
11
+ response.status
25
12
  end
26
13
 
27
14
  def success?
28
- @response.success?
15
+ response.success?
29
16
  end
30
17
 
31
- def failed?
18
+ def fail?
32
19
  !success?
33
20
  end
34
21
 
35
- def has_canonical_ids?
36
- (response_body.canonical_ids || 0) > 0
22
+ def results
23
+ @_results ||= begin
24
+ results = to_h.fetch 'results', []
25
+ results.map!.with_index do |attributes, i|
26
+ GCM::Result.new attributes, tokens[i]
27
+ end
28
+ ResultCollection.new(results)
29
+ end
37
30
  end
38
31
 
39
- def canonical_ids
40
- response_body.results.map do |result|
41
- result["registration_id"]
32
+ def error
33
+ case status
34
+ when 401
35
+ 'Authentication error with GCM. Check the server whitelist and the validity of your project key.'
36
+ when 400
37
+ 'Invalid JSON was sent to GCM.'
38
+ when 500..599
39
+ 'GCM Internal server error.'
40
+ else
41
+ nil
42
42
  end
43
43
  end
44
44
 
45
- def response_body
46
- @response_body ||= OpenStruct.new JSON.parse(response.body)
45
+ def retry_after
46
+ response.headers['Retry-After']
47
+ end
48
+
49
+ def to_h
50
+ JSON.parse response.body
51
+ rescue JSON::ParserError
52
+ {}
47
53
  end
48
54
  end
49
55
  end
@@ -0,0 +1,28 @@
1
+ module GCM
2
+ class ResponseCollection
3
+ attr_reader :recommendations
4
+
5
+ def initialize(notification, responses = [])
6
+ @responses = responses
7
+ @notification = notification
8
+ end
9
+
10
+ def [](index)
11
+ @responses[index]
12
+ end
13
+
14
+ def <<(response)
15
+ @responses.concat Array(response).flatten
16
+ end
17
+
18
+ def results
19
+ out = GCM::ResultCollection.new
20
+ @responses.map do |response|
21
+ response.results.each do |result|
22
+ out << result
23
+ end
24
+ end
25
+ out
26
+ end
27
+ end
28
+ end
@@ -1,30 +1,29 @@
1
1
  module GCM
2
2
  class Result
3
- attr_reader :responses
3
+ include ActiveModel::Model
4
4
 
5
- def initialize(notification)
6
- @notification = notification
7
- @responses = []
8
- end
5
+ attr_accessor :message_id, :registration_id, :error
6
+ attr_reader :token
9
7
 
10
- def success?
11
- failed_responses.empty?
8
+ def initialize(attributes, token)
9
+ super attributes
10
+ @token = token
12
11
  end
13
12
 
14
- def process_response(response, device_tokens)
15
- self.responses << GCM::Response.new(response, device_tokens)
13
+ def success?
14
+ message_id.present?
16
15
  end
17
16
 
18
- def failed_responses
19
- @_failed_responses ||= responses.select(&:failed?)
17
+ def fail?
18
+ !success?
20
19
  end
21
20
 
22
- def failed_device_tokens
23
- @_failed_device_tokens ||= failed_responses.flat_map { |response| response.device_tokens }
21
+ def canonical_id
22
+ registration_id
24
23
  end
25
24
 
26
- def has_canonical_ids?
27
- responses.map{ |response| response.has_canonical_ids? }.reduce{ |carry, val| carry || val }
25
+ def has_canonical_id?
26
+ canonical_id.present?
28
27
  end
29
28
  end
30
29
  end
@@ -0,0 +1,33 @@
1
+ module GCM
2
+ class ResultCollection
3
+ include Enumerable
4
+
5
+ def initialize(results = [])
6
+ @results = results
7
+ end
8
+
9
+ def [](index)
10
+ @results[index]
11
+ end
12
+
13
+ def each(&block)
14
+ @results.each &block
15
+ end
16
+
17
+ def succeeded
18
+ @results.select &:success?
19
+ end
20
+
21
+ def failed
22
+ @results.select &:fail?
23
+ end
24
+
25
+ def with_canonical_ids
26
+ @results.select &:has_canonical_id?
27
+ end
28
+
29
+ def <<(result)
30
+ @results << result
31
+ end
32
+ end
33
+ end
@@ -2,8 +2,7 @@ module GCM
2
2
  class Service
3
3
  include ActiveModel::Model
4
4
 
5
- MAX_NUMBER_OF_RETRIES = 3
6
- MAX_DEVICES_AT_ONCE = 999
5
+ BATCH_SIZE = 999
7
6
 
8
7
  attr_accessor :host, :key
9
8
  attr_reader :connection, :attempts
@@ -16,27 +15,27 @@ module GCM
16
15
  @attempts = 0
17
16
  end
18
17
 
19
- def deliver(notification, *device_tokens)
20
- result = GCM::Result.new(notification)
21
- device_tokens = Array(device_tokens).flatten
22
- device_tokens.each_slice(MAX_DEVICES_AT_ONCE) do |tokens|
23
- payload = notification.to_h.merge registration_ids: tokens
24
- result.process_response connection.write(payload), tokens
25
- end
26
- result
27
- end
28
-
29
- def deliver_topic(notification, topic)
30
- GCM::Result.new(notification).tap do |result|
31
- result.process_response connection.write(notification.to_h.merge(to: topic)), [topic]
32
- end
18
+ def deliver(notification, *tokens, topic: nil)
19
+ responses = GCM::ResponseCollection.new(notification)
20
+ responses << deliver_to_tokens(notification, tokens)
21
+ responses << deliver_to_topic(notification, topic) if topic
22
+ responses
33
23
  end
34
24
 
35
25
  private
26
+ def each_batch_of_tokens(tokens, batch_size: BATCH_SIZE, &block)
27
+ Array(tokens).flatten.compact.each_slice(batch_size)
28
+ end
36
29
 
37
- def too_many_retries?
38
- @attempts >= MAX_NUMBER_OF_RETRIES
30
+ def deliver_to_tokens(notification, tokens)
31
+ each_batch_of_tokens(tokens).map do |tokens|
32
+ payload = notification.to_h.merge registration_ids: tokens
33
+ GCM::Response.new(connection.write(payload), tokens)
34
+ end
39
35
  end
40
36
 
37
+ def deliver_to_topic(notification, topic)
38
+ GCM::Response.new connection.write(notification.to_h.merge(to: topic))
39
+ end
41
40
  end
42
41
  end
@@ -1,3 +1,3 @@
1
1
  module Mercurius
2
- VERSION = '0.1.5'
2
+ VERSION = '0.1.6'
3
3
  end
data/mercurius.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.files = `git ls-files`.split("\n")
20
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.executables = `git ls-files -- exe/*`.split("\n").map{ |f| File.basename(f) }
22
22
 
23
23
  s.require_paths = ["lib"]
24
24
  s.required_ruby_version = '~> 2.0'
@@ -1,17 +1,11 @@
1
1
  describe GCM::Notification do
2
2
  describe 'with attributes' do
3
- it 'should extract special attributes from the hash and assign them' do
3
+ it 'delegates methods to the attributes hash' do
4
4
  notification = GCM::Notification.new({ collapse_key: 'some_key', time_to_live: 123, delay_while_idle: true, dry_run: true })
5
5
  expect(notification.collapse_key).to eq 'some_key'
6
6
  expect(notification.time_to_live).to eq 123
7
7
  expect(notification.delay_while_idle).to eq true
8
8
  expect(notification.dry_run).to eq true
9
9
  end
10
-
11
- it 'should move other attributes to data' do
12
- notification = GCM::Notification.new({ message: 'hello', dry_run: true})
13
- expect(notification.dry_run).to eq true
14
- expect(notification.data).to eq({ message: 'hello' })
15
- end
16
10
  end
17
- end
11
+ end
@@ -1,25 +1,82 @@
1
1
  describe GCM::Response do
2
- subject { GCM::Response.new nil, nil }
3
-
4
- it 'has_canonical_ids? should return true if the response body has canonical ids' do
5
- allow(subject).to receive(:response_body) { OpenStruct.new canonical_ids: 1 }
6
- expect(subject.has_canonical_ids?).to be_truthy
2
+ class FakeResponse < Struct.new(:hash, :success)
3
+ def body; hash.to_json; end
4
+ def success?; success; end
7
5
  end
8
6
 
9
- it 'has_canonical_ids? should return false if the body has no canonical ids' do
10
- allow(subject).to receive(:response_body) { OpenStruct.new success: 1 }
11
- expect(subject.has_canonical_ids?).to be_falsy
7
+ let(:json) { Hash.new }
8
+ let(:success) { true }
9
+ let(:tokens) { ['token123', 'token456'] }
10
+
11
+ let(:response) do
12
+ GCM::Response.new FakeResponse.new(json, success), tokens
12
13
  end
13
14
 
14
- it 'canonical_ids should return an array of registration ids' do
15
- allow(subject).to receive(:response_body) { OpenStruct.new results: [{'registration_id' => '1234'}, {'registration_id' => '5678'}, {}] }
16
- expect(subject.canonical_ids).to eq ['1234', '5678', nil]
15
+ describe '#results' do
16
+ let(:json) do
17
+ {
18
+ multicast_id: 108, success: 2, failure: 1, canonical_ids: 1, results: [
19
+ { message_id: '1' },
20
+ { message_id: '2', registration_id: 'canonicalToken456' },
21
+ { error: 'DeviceMessageRateExceeded' }
22
+ ]
23
+ }
24
+ end
25
+
26
+ it 'returns an array of the results in a response' do
27
+ expect(response.results.count).to eq 3
28
+ expect(response.results[0].message_id).to eq '1'
29
+ expect(response.results[0].token).to eq 'token123'
30
+ expect(response.results[1].message_id).to eq '2'
31
+ expect(response.results[1].token).to eq 'token456'
32
+ expect(response.results[1].canonical_id).to eq 'canonicalToken456'
33
+ end
34
+
35
+ describe 'ResultCollection#succeeded' do
36
+ subject { response.results.succeeded }
37
+
38
+ it 'returns only the successful results' do
39
+ expect(subject.count).to eq 2
40
+ expect(subject[0].message_id).to eq '1'
41
+ expect(subject[1].message_id).to eq '2'
42
+ end
43
+ end
44
+
45
+ describe 'ResultCollection#failed' do
46
+ subject { response.results.failed }
47
+
48
+ it 'returns only the failed results' do
49
+ expect(subject.count).to eq 1
50
+ expect(subject[0].error).to eq 'DeviceMessageRateExceeded'
51
+ end
52
+ end
53
+
54
+ describe 'ResultCollection#with_canonical_ids' do
55
+ subject { response.results.with_canonical_ids }
56
+
57
+ it 'returns only the results with canonical ids' do
58
+ expect(subject.count).to eq 1
59
+ expect(subject[0].token).to eq 'token456'
60
+ expect(subject[0].canonical_id).to eq 'canonicalToken456'
61
+ end
62
+ end
17
63
  end
18
64
 
19
- it 'response_body should return an openstruct of the response.body' do
20
- allow(subject).to receive(:response) { OpenStruct.new body: { one: 'two' }.to_json }
21
- result = subject.response_body
22
- expect(result).to be_a OpenStruct
23
- expect(result.to_h).to eq({one: 'two'})
65
+ describe '#success?' do
66
+ context 'Faraday response is successful' do
67
+ let(:success) { true }
68
+
69
+ it 'returns true' do
70
+ expect(response).to be_success
71
+ end
72
+ end
73
+
74
+ context 'Faraday response is not successful' do
75
+ let(:success) { false }
76
+
77
+ it 'returns false' do
78
+ expect(response).to_not be_success
79
+ end
80
+ end
24
81
  end
25
- end
82
+ end
@@ -0,0 +1,32 @@
1
+ describe GCM::ResultCollection do
2
+ subject do
3
+ GCM::ResultCollection.new [
4
+ GCM::Result.new({ message_id: '1' }, 'token123'),
5
+ GCM::Result.new({ message_id: '2', registration_id: 'canonicalToken456' }, 'token456'),
6
+ GCM::Result.new({ error: 'DeviceMessageRateExceeded' }, nil)
7
+ ]
8
+ end
9
+
10
+ describe '#succeeded' do
11
+ it 'returns only the successful results' do
12
+ expect(subject.succeeded.count).to eq 2
13
+ expect(subject.succeeded[0].message_id).to eq '1'
14
+ expect(subject.succeeded[1].message_id).to eq '2'
15
+ end
16
+ end
17
+
18
+ describe '#failed' do
19
+ it 'returns only the failed results' do
20
+ expect(subject.failed.count).to eq 1
21
+ expect(subject.failed[0].error).to eq 'DeviceMessageRateExceeded'
22
+ end
23
+ end
24
+
25
+ describe '#with_canonical_ids' do
26
+ it 'returns only the results with canonical ids' do
27
+ expect(subject.with_canonical_ids.count).to eq 1
28
+ expect(subject.with_canonical_ids[0].token).to eq 'token456'
29
+ expect(subject.with_canonical_ids[0].canonical_id).to eq 'canonicalToken456'
30
+ end
31
+ end
32
+ end
@@ -1,13 +1,49 @@
1
1
  describe GCM::Result do
2
- subject { GCM::Result.new nil }
2
+ describe '#canonical_id' do
3
+ it 'returns the registration_id from the GCM response JSON' do
4
+ result = GCM::Result.new({ message_id: '1:08', registration_id: 'canonicalToken123' }, nil)
5
+ expect(result.canonical_id).to eq 'canonicalToken123'
6
+ end
3
7
 
4
- it 'has_canonical_ids? should return true if any responses have them' do
5
- allow(subject).to receive(:responses) { [instance_double(GCM::Response, has_canonical_ids?: true), instance_double(GCM::Response, has_canonical_ids?: false)] }
6
- expect(subject.has_canonical_ids?).to be_truthy
8
+ it 'returns nil if no canonical ID is given by GCM' do
9
+ result = GCM::Result.new({ message_id: '1:08' }, nil)
10
+ expect(result.canonical_id).to be_nil
11
+ end
7
12
  end
8
13
 
9
- it 'otherwise, has_canonical_ids should return false' do
10
- allow(subject).to receive(:responses) { [instance_double(GCM::Response, has_canonical_ids?: false), instance_double(GCM::Response, has_canonical_ids?: false)] }
11
- expect(subject.has_canonical_ids?).to be_falsy
14
+ describe '#has_canonical_id?' do
15
+ it 'returns true when canonical_id is given by GCM' do
16
+ result = GCM::Result.new({ message_id: '1:08', registration_id: 'canonicalToken123' }, nil)
17
+ expect(result.has_canonical_id?).to be_truthy
18
+ end
19
+
20
+ it 'returns false when canonical_id is not given by GCM' do
21
+ result = GCM::Result.new({ message_id: '1:08' }, nil)
22
+ expect(result.has_canonical_id?).to be_falsey
23
+ end
24
+ end
25
+
26
+ describe '#success?' do
27
+ it 'returns true when the message is sent and returned with a message id' do
28
+ result = GCM::Result.new({ message_id: '1:08' }, nil)
29
+ expect(result).to be_success
30
+ end
31
+
32
+ it 'returns false otherwise' do
33
+ result = GCM::Result.new({ error: 'DeviceMessageRateExceeded' }, nil)
34
+ expect(result).to_not be_success
35
+ end
36
+ end
37
+
38
+ describe '#error' do
39
+ it 'returns the error from GCM' do
40
+ result = GCM::Result.new({ error: 'DeviceMessageRateExceeded' }, nil)
41
+ expect(result.error).to eq 'DeviceMessageRateExceeded'
42
+ end
43
+
44
+ it 'returns nil when there is no error' do
45
+ result = GCM::Result.new({ message_id: '1:08' }, nil)
46
+ expect(result.error).to be_nil
47
+ end
12
48
  end
13
49
  end
@@ -1,6 +1,6 @@
1
1
  describe GCM::Service do
2
2
  let(:service) { GCM::Service.new }
3
- let(:message) { GCM::Notification.new(alert: 'Hey') }
3
+ let(:message) { GCM::Notification.new(data: { alert: 'Hey' }) }
4
4
 
5
5
  it 'should default to the GCM module configs' do
6
6
  expect(service.host).to eq GCM.host
@@ -34,7 +34,7 @@ describe GCM::Service do
34
34
  expect(WebMock).to have_requested(:post, %r[android.googleapis.com/gcm/send]).
35
35
  with(body: { data: { alert: 'Hey' }, registration_ids: tokens.take(999) })
36
36
  expect(WebMock).to have_requested(:post, %r[android.googleapis.com/gcm/send]).
37
- with(body: { data: { alert: 'Hey' }, registration_ids: [tokens.last] })
37
+ with(body: { data: { alert: 'Hey' }, registration_ids: ['token1000'] })
38
38
  end
39
39
  end
40
40
 
@@ -43,7 +43,7 @@ describe GCM::Service do
43
43
  let(:message) { { notification: { title: 'hello', body: 'world' } } }
44
44
 
45
45
  it 'sends a notification to a topic' do
46
- service.deliver_topic message, '/topics/global'
46
+ service.deliver message, topic: '/topics/global'
47
47
  expect(WebMock).to have_requested(:post, %r[android.googleapis.com/gcm/send]).
48
48
  with(body: message.merge(to: '/topics/global'))
49
49
  end
@@ -54,28 +54,47 @@ describe GCM::Service do
54
54
  before { stub_request :post, %r[android.googleapis.com/gcm/send] }
55
55
 
56
56
  it 'processes a 200 response' do
57
- result = service.deliver message, 'token123'
58
- expect(result.responses[0].status).to eq 200
59
- expect(result.responses[0].message).to eq GCM::Response::MESSAGES[200]
60
- expect(result.failed_device_tokens).to eq []
57
+ responses = service.deliver message, 'token123'
58
+ expect(responses[0].status).to eq 200
59
+ expect(responses[0].success?).to eq true
60
+ expect(responses.results.failed).to eq []
61
61
  end
62
62
  end
63
63
 
64
- context 'failure' do
65
- before { stub_request(:post, %r[android.googleapis.com/gcm/send]).to_return(status: 400) }
64
+ describe 'failure' do
65
+ context 401 do
66
+ before { stub_request(:post, %r[android.googleapis.com/gcm/send]).to_return(status: 401) }
66
67
 
67
- it 'processes a 400 response' do
68
- result = service.deliver message, 'token123'
69
- expect(result.responses[0].status).to eq 400
70
- expect(result.responses[0].message).to eq GCM::Response::MESSAGES[400]
71
- expect(result.failed_device_tokens).to eq ['token123']
68
+ it 'has a meaningful message from #error' do
69
+ responses = service.deliver message, 'token123'
70
+ expect(responses[0].error).to match /Authentication error/
71
+ end
72
72
  end
73
73
 
74
- it 'adds all failed messages to the failed device tokens array' do
75
- tokens = (1..1000).to_a.map { |i| "token#{i}" }
76
- result = service.deliver message, tokens
77
- expect(result.failed_device_tokens).to include 'token1'
78
- expect(result.failed_device_tokens).to include 'token1000'
74
+ context 400 do
75
+ before { stub_request(:post, %r[android.googleapis.com/gcm/send]).to_return(status: 400) }
76
+
77
+ it 'has a meaningful message from #error' do
78
+ responses = service.deliver message, 'token123'
79
+ expect(responses[0].error).to match /Invalid JSON/
80
+ end
81
+ end
82
+
83
+ context 500..599 do
84
+ before do
85
+ stub_request(:post, %r[android.googleapis.com/gcm/send]).
86
+ to_return status: 500, headers: { 'Retry-After' => '120' }
87
+ end
88
+
89
+ it 'has a meaningful message from #error' do
90
+ responses = service.deliver message, 'token123'
91
+ expect(responses[0].error).to match /GCM Internal server error/
92
+ end
93
+
94
+ it 'returns the Retry-After header from GCM' do
95
+ responses = service.deliver message, 'token123'
96
+ expect(responses[0].retry_after).to eq '120'
97
+ end
79
98
  end
80
99
  end
81
100
 
@@ -18,7 +18,7 @@ describe 'Test mode' do
18
18
 
19
19
  context 'GCM' do
20
20
  let(:service) { GCM::MockService.new }
21
- let(:message) { GCM::Notification.new(alert: 'Hey') }
21
+ let(:message) { GCM::Notification.new(data: { alert: 'Hey' }) }
22
22
 
23
23
  it 'returns the deliveries sent to GCM' do
24
24
  result = service.deliver message, 'token123'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mercurius
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Beck
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-09 00:00:00.000000000 Z
11
+ date: 2015-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -139,6 +139,8 @@ files:
139
139
  - LICENSE
140
140
  - README.md
141
141
  - Rakefile
142
+ - bin/rake
143
+ - bin/rspec
142
144
  - lib/mercurius.rb
143
145
  - lib/mercurius/apns.rb
144
146
  - lib/mercurius/apns/connection.rb
@@ -153,7 +155,9 @@ files:
153
155
  - lib/mercurius/gcm/connection.rb
154
156
  - lib/mercurius/gcm/notification.rb
155
157
  - lib/mercurius/gcm/response.rb
158
+ - lib/mercurius/gcm/response_collection.rb
156
159
  - lib/mercurius/gcm/result.rb
160
+ - lib/mercurius/gcm/result_collection.rb
157
161
  - lib/mercurius/gcm/service.rb
158
162
  - lib/mercurius/testing.rb
159
163
  - lib/mercurius/testing/base.rb
@@ -167,6 +171,7 @@ files:
167
171
  - spec/lib/apns_spec.rb
168
172
  - spec/lib/gcm_notification_spec.rb
169
173
  - spec/lib/gcm_response_spec.rb
174
+ - spec/lib/gcm_result_collection_spec.rb
170
175
  - spec/lib/gcm_result_spec.rb
171
176
  - spec/lib/gcm_service_spec.rb
172
177
  - spec/lib/testing_spec.rb
@@ -202,6 +207,7 @@ test_files:
202
207
  - spec/lib/apns_spec.rb
203
208
  - spec/lib/gcm_notification_spec.rb
204
209
  - spec/lib/gcm_response_spec.rb
210
+ - spec/lib/gcm_result_collection_spec.rb
205
211
  - spec/lib/gcm_result_spec.rb
206
212
  - spec/lib/gcm_service_spec.rb
207
213
  - spec/lib/testing_spec.rb