sparqcode-urbanairship 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2011 Groupon, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,132 @@
1
+ Urbanairship is a Ruby library for interacting with the [Urban Airship API](http://urbanairship.com).
2
+
3
+ Installation
4
+ ============
5
+ gem install urbanairship
6
+
7
+ Note: if you are using Ruby 1.8, you should also install the ```system_timer``` gem for more reliable timeout behaviour. See http://ph7spot.com/musings/system-timer for more information.
8
+
9
+ Configuration
10
+ =============
11
+ ```ruby
12
+ Urbanairship.application_key = 'application-key'
13
+ Urbanairship.application_secret = 'application-secret'
14
+ Urbanairship.master_secret = 'master-secret'
15
+ Urbanairship.logger = Rails.logger
16
+ Urbanairship.request_timeout = 5 # default
17
+ ```
18
+
19
+ Usage
20
+ =====
21
+
22
+ Registering a device token
23
+ --------------------------
24
+ ```ruby
25
+ Urbanairship.register_device('DEVICE-TOKEN')
26
+ ```
27
+
28
+ Unregistering a device token
29
+ ----------------------------
30
+ ```ruby
31
+ Urbanairship.unregister_device('DEVICE-TOKEN')
32
+ ```
33
+
34
+ Sending a push notification
35
+ ---------------------------
36
+ ```ruby
37
+ notification = {
38
+ :schedule_for => [1.hour.from_now],
39
+ :device_tokens => ['DEVICE-TOKEN-ONE', 'DEVICE-TOKEN-TWO'],
40
+ :aps => {:alert => 'You have a new message!', :badge => 1}
41
+ }
42
+
43
+ Urbanairship.push(notification) # =>
44
+ # {
45
+ # "scheduled_notifications" => ["https://go.urbanairship.com/api/push/scheduled/123456"]
46
+ # }
47
+ ```
48
+
49
+ Batching push notification sends
50
+ --------------------------------
51
+ ```ruby
52
+ notifications = [
53
+ {
54
+ :schedule_for => [{ :alias => 'deadbeef', :scheduled_time => 1.hour.from_now }],
55
+ :device_tokens => ['DEVICE-TOKEN-ONE', 'DEVICE-TOKEN-TWO'],
56
+ :aps => {:alert => 'You have a new message!', :badge => 1}
57
+ },
58
+ {
59
+ :schedule_for => [3.hours.from_now],
60
+ :device_tokens => ['DEVICE-TOKEN-THREE'],
61
+ :aps => {:alert => 'You have a new message!', :badge => 1}
62
+ }
63
+ ]
64
+
65
+ Urbanairship.batch_push(notifications)
66
+ ```
67
+
68
+
69
+ Sending broadcast notifications
70
+ -------------------------------
71
+ Urban Airship allows you to send a broadcast notification to all active registered device tokens for your app.
72
+
73
+ ```ruby
74
+ notification = {
75
+ :schedule_for => [1.hour.from_now],
76
+ :aps => {:alert => 'Important announcement!', :badge => 1}
77
+ }
78
+
79
+ Urbanairship.broadcast_push(notification)
80
+ ```
81
+
82
+ Polling the feedback API
83
+ ------------------------
84
+ The first time you attempt to send a push notification to a device that has uninstalled your app (or has opted-out of notifications), both Apple and Urban Airship will register that token in their feedback API. Urban Airship will prevent further attempted notification sends to that device, but it's a good practice to periodically poll Urban Airship's feedback API and mark those tokens as inactive in your own system as well.
85
+
86
+ ```ruby
87
+ # find all device tokens deactivated in the past 24 hours
88
+ Urbanairship.feedback(24.hours.ago) # =>
89
+ # [
90
+ # {
91
+ # "marked_inactive_on"=>"2011-06-03 22:53:23",
92
+ # "alias"=>nil,
93
+ # "device_token"=>"DEVICE-TOKEN-ONE"
94
+ # },
95
+ # {
96
+ # "marked_inactive_on"=>"2011-06-03 22:53:23",
97
+ # "alias"=>nil,
98
+ # "device_token"=>"DEVICE-TOKEN-TWO"
99
+ # }
100
+ # ]
101
+ ```
102
+
103
+ Deleting scheduled notifications
104
+ --------------------------------
105
+
106
+ If you know the alias or id of a scheduled push notification then you can delete it from Urban Airship's queue and it will not be delivered.
107
+
108
+ ```ruby
109
+ Urbanairship.delete_scheduled_push("123456789")
110
+ Urbanairship.delete_scheduled_push(123456789)
111
+ Urbanairship.delete_scheduled_push(:alias => "deadbeef")
112
+ ```
113
+
114
+ -----------------------------
115
+
116
+ Note: all public library methods will return either an array or a hash, depending on the response from the Urban Airship API. In addition, you can inspect these objects to find out if they were successful or not, and what the http response code from Urban Airship was.
117
+
118
+ ```ruby
119
+ response = Urbanairship.push(payload)
120
+ response.success? # => true
121
+ response.code # => '200'
122
+ response.inspect # => "{\"scheduled_notifications\"=>[\"https://go.urbanairship.com/api/push/scheduled/123456\"]}"
123
+ ```
124
+
125
+ If the call to Urban Airship times out, you'll get a response object with a '503' code.
126
+
127
+ ```ruby
128
+ response = Urbanairship.feedback(1.year.ago)
129
+ response.success? # => false
130
+ response.code # => '503'
131
+ response.inspect # => "{\"error\"=>\"Request timeout\"}"
132
+ ```
@@ -0,0 +1,8 @@
1
+ desc 'Run all the tests'
2
+ task :default => :spec
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new do |t|
6
+ t.rspec_opts = ['-c', '-f progress', '-r ./spec/spec_helper.rb']
7
+ t.pattern = 'spec/**/*_spec.rb'
8
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'urbanairship/service')
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), 'service')
2
+
3
+ module Urbanairship
4
+ class Base
5
+ include Service
6
+
7
+ def initialize(application_key, application_secret, master_secret)
8
+ self.application_key = application_key
9
+ self.application_secret = application_secret
10
+ self.master_secret = master_secret
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ @@applications = { }
17
+
18
+ def self.[](application_id)
19
+ @@applications[application_id]
20
+ end
21
+
22
+ def self.add_service(application_id, application_key, application_secret, master_secret)
23
+ new_service = Base.new(application_key, application_secret, master_secret)
24
+ yield new_service if block_given?
25
+ @@applications[application_id] = new_service
26
+ end
27
+
28
+ end
@@ -0,0 +1,33 @@
1
+ module Urbanairship
2
+ module Response
3
+ module InstanceMethods
4
+ attr_accessor :ua_response, :ua_options
5
+
6
+ def code
7
+ (ua_options[:code] || ua_response.code).to_s
8
+ end
9
+
10
+ def success?
11
+ !!(code =~ /^2/)
12
+ end
13
+ end
14
+
15
+ def self.wrap(response, options = {})
16
+ if options[:body]
17
+ output = options[:body]
18
+ else
19
+ begin
20
+ output = JSON.parse(response.body || '{}')
21
+ rescue JSON::ParserError
22
+ output = {}
23
+ end
24
+ end
25
+
26
+ output.extend(Urbanairship::Response::InstanceMethods)
27
+ output.ua_response = response
28
+ output.ua_options = options
29
+
30
+ return output
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,124 @@
1
+ require 'json'
2
+ require 'net/https'
3
+ require 'time'
4
+
5
+ require File.join(File.dirname(__FILE__), 'response')
6
+
7
+ module Urbanairship
8
+ begin
9
+ require 'system_timer'
10
+ Timer = SystemTimer
11
+ rescue LoadError
12
+ require 'timeout'
13
+ Timer = Timeout
14
+ end
15
+
16
+ module Service
17
+ attr_accessor :application_key, :application_secret, :master_secret, :logger, :request_timeout
18
+
19
+ def register_device(device_token, options = {})
20
+ body = parse_register_options(options).to_json
21
+ do_request(:put, "/api/device_tokens/#{device_token}", :body => body, :authenticate_with => :application_secret)
22
+ end
23
+
24
+ def unregister_device(device_token)
25
+ do_request(:delete, "/api/device_tokens/#{device_token}", :authenticate_with => :application_secret)
26
+ end
27
+
28
+ def delete_scheduled_push(param)
29
+ path = param.is_a?(Hash) ? "/api/push/scheduled/alias/#{param[:alias].to_s}" : "/api/push/scheduled/#{param.to_s}"
30
+ do_request(:delete, path, :authenticate_with => :master_secret)
31
+ end
32
+
33
+ def push(options = {})
34
+ body = parse_push_options(options).to_json
35
+ do_request(:post, "/api/push/", :body => body, :authenticate_with => :master_secret)
36
+ end
37
+
38
+ def batch_push(notifications = [])
39
+ body = notifications.map{|notification| parse_push_options(notification)}.to_json
40
+ do_request(:post, "/api/push/batch/", :body => body, :authenticate_with => :master_secret)
41
+ end
42
+
43
+ def broadcast_push(options = {})
44
+ body = parse_push_options(options).to_json
45
+ do_request(:post, "/api/push/broadcast/", :body => body, :authenticate_with => :master_secret)
46
+ end
47
+
48
+ def feedback(time)
49
+ do_request(:get, "/api/device_tokens/feedback/?since=#{format_time(time)}", :authenticate_with => :master_secret)
50
+ end
51
+
52
+ private
53
+
54
+ def do_request(http_method, path, options = {})
55
+ verify_configuration_values(:application_key, options[:authenticate_with])
56
+
57
+ klass = Net::HTTP.const_get(http_method.to_s.capitalize)
58
+
59
+ request = klass.new(path)
60
+ request.basic_auth @application_key, instance_variable_get("@#{options[:authenticate_with]}")
61
+ request.add_field "Content-Type", "application/json"
62
+ request.body = options[:body] if options[:body]
63
+
64
+ Timer.timeout(request_timeout) do
65
+ start_time = Time.now
66
+ response = http_client.request(request)
67
+ log_request_and_response(request, response, Time.now - start_time)
68
+ Urbanairship::Response.wrap(response)
69
+ end
70
+ rescue Timeout::Error
71
+ logger.error "Urbanairship request timed out after #{request_timeout} seconds: [#{http_method} #{request.path} #{request.body}]"
72
+ Urbanairship::Response.wrap(nil, :body => {:error => 'Request timeout'}, :code => '503')
73
+ end
74
+
75
+ def verify_configuration_values(*symbols)
76
+ absent_values = symbols.select{|symbol| instance_variable_get("@#{symbol}").nil? }
77
+ raise("Must configure #{absent_values.join(", ")} before making this request.") unless absent_values.empty?
78
+ end
79
+
80
+ def http_client
81
+ Net::HTTP.new("go.urbanairship.com", 443).tap{|http| http.use_ssl = true}
82
+ end
83
+
84
+ def parse_register_options(hash = {})
85
+ hash[:alias] = hash[:alias].to_s unless hash[:alias].nil?
86
+ hash
87
+ end
88
+
89
+ def parse_push_options(hash = {})
90
+ hash[:schedule_for] = hash[:schedule_for].map{|elem| process_scheduled_elem(elem)} unless hash[:schedule_for].nil?
91
+ hash
92
+ end
93
+
94
+ def log_request_and_response(request, response, time)
95
+ return if logger.nil?
96
+
97
+ time = (time * 1000).to_i
98
+ http_method = request.class.to_s.split('::')[-1]
99
+ logger.info "Urbanairship (#{time}ms): [#{http_method} #{request.path}, #{request.body}], [#{response.code}, #{response.body}]"
100
+ logger.flush if logger.respond_to?(:flush)
101
+ end
102
+
103
+ def format_time(time)
104
+ time = Time.parse(time) if time.is_a?(String)
105
+ time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
106
+ end
107
+
108
+ def process_scheduled_elem(elem)
109
+ if elem.class == Hash
110
+ elem.merge!(:scheduled_time => format_time(elem[:scheduled_time]))
111
+ else
112
+ format_time(elem)
113
+ end
114
+ end
115
+
116
+ def request_timeout
117
+ @request_timeout || 5.0
118
+ end
119
+ end
120
+
121
+ extend Service
122
+
123
+ end
124
+
@@ -0,0 +1,77 @@
1
+ describe Urbanairship::Response do
2
+
3
+ before do
4
+ FakeWeb.allow_net_connect = false
5
+ end
6
+
7
+ context "#code" do
8
+ subject { Urbanairship.register_device("new_device_token") }
9
+
10
+ before do
11
+ FakeWeb.register_uri(:put, "https://my_app_key:my_app_secret@go.urbanairship.com/api/device_tokens/new_device_token", :status => ["201", "Created"])
12
+ FakeWeb.register_uri(:put, /bad_key\:my_app_secret\@go\.urbanairship\.com/, :status => ["401", "Unauthorized"])
13
+ Urbanairship.application_secret = "my_app_secret"
14
+ end
15
+
16
+ it "should be set correctly on new registraion token" do
17
+ Urbanairship.application_key = "my_app_key"
18
+ subject.code.should eql '201'
19
+ end
20
+
21
+ it "should set correctly on unauthhorized" do
22
+ Urbanairship.application_key = "bad_key"
23
+ subject.code.should eql '401'
24
+ end
25
+ end
26
+
27
+ context "#success?" do
28
+ subject { Urbanairship.register_device("new_device_token") }
29
+
30
+ before do
31
+ FakeWeb.register_uri(:put, "https://my_app_key:my_app_secret@go.urbanairship.com/api/device_tokens/new_device_token", :status => ["201", "Created"])
32
+ FakeWeb.register_uri(:put, /bad_key\:my_app_secret\@go\.urbanairship\.com/, :status => ["401", "Unauthorized"])
33
+ Urbanairship.application_secret = "my_app_secret"
34
+ end
35
+
36
+ it "should be true with good key" do
37
+ Urbanairship.application_key = "my_app_key"
38
+ subject.success?.should == true
39
+ end
40
+
41
+ it "should be false with bad key" do
42
+ Urbanairship.application_key = "bad_key"
43
+ subject.success?.should == false
44
+ end
45
+ end
46
+
47
+ context "body array accessor" do
48
+ subject { Urbanairship.feedback(Time.now) }
49
+
50
+ before do
51
+ FakeWeb.register_uri(:get, /my_app_key\:my_master_secret\@go\.urbanairship.com\/api\/device_tokens\/feedback/, :status => ["200", "OK"], :body => "[{\"device_token\":\"token\",\"marked_inactive_on\":\"2010-10-14T19:15:13Z\",\"alias\":\"my_alias\"},{\"device_token\":\"token2\",\"marked_inactive_on\":\"2010-10-14T19:15:13Z\",\"alias\":\"my_alias\"}]")
52
+ Urbanairship.application_secret = "my_app_secret"
53
+ Urbanairship.application_key = "my_app_key"
54
+ Urbanairship.master_secret = "my_master_secret"
55
+ end
56
+
57
+ it "should set up correct indexes" do
58
+ subject[0]['device_token'].should eql "token"
59
+ subject[1]['device_token'].should eql "token2"
60
+ end
61
+ end
62
+
63
+ context "non array response" do
64
+ subject { Urbanairship.feedback(Time.now) }
65
+
66
+ before do
67
+ FakeWeb.register_uri(:get, /my_app_key\:my_master_secret\@go\.urbanairship.com\/api\/device_tokens\/feedback/, :status => ["200", "OK"], :body => "{\"device_token\":\"token\",\"marked_inactive_on\":\"2010-10-14T19:15:13Z\",\"alias\":\"my_alias\"}")
68
+ Urbanairship.application_secret = "my_app_secret"
69
+ Urbanairship.application_key = "my_app_key"
70
+ Urbanairship.master_secret = "my_master_secret"
71
+ end
72
+
73
+ it "should set up correct keys" do
74
+ subject['device_token'].should eql "token"
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,17 @@
1
+ require 'base64'
2
+ require 'fakeweb'
3
+
4
+ require File.join(File.dirname(__FILE__), '/../lib/urbanairship')
5
+
6
+
7
+ RSpec.configure do |config|
8
+ config.after(:each) do
9
+ # reset configuration
10
+ Urbanairship.application_key = nil
11
+ Urbanairship.application_secret = nil
12
+ Urbanairship.master_secret = nil
13
+ Urbanairship.logger = nil
14
+
15
+ FakeWeb.instance_variable_set("@last_request", nil)
16
+ end
17
+ end
@@ -0,0 +1,456 @@
1
+ describe Urbanairship do
2
+ before(:all) do
3
+ FakeWeb.allow_net_connect = false
4
+
5
+ # register_device
6
+ FakeWeb.register_uri(:put, "https://my_app_key:my_app_secret@go.urbanairship.com/api/device_tokens/new_device_token", :status => ["201", "Created"])
7
+ FakeWeb.register_uri(:put, "https://my_app_key:my_app_secret@go.urbanairship.com/api/device_tokens/existing_device_token", :status => ["200", "OK"])
8
+ FakeWeb.register_uri(:put, "https://my_app_key:my_app_secret@go.urbanairship.com/api/device_tokens/device_token_one", :status => ["201", "Created"])
9
+ FakeWeb.register_uri(:put, /bad_key\:my_app_secret\@go\.urbanairship\.com/, :status => ["401", "Unauthorized"])
10
+
11
+ # unregister_device
12
+ FakeWeb.register_uri(:delete, /my_app_key\:my_app_secret\@go\.urbanairship.com\/api\/device_tokens\/.+/, :status => ["204", "No Content"])
13
+ FakeWeb.register_uri(:delete, /bad_key\:my_app_secret\@go\.urbanairship.com\/api\/device_tokens\/.+/, :status => ["401", "Unauthorized"])
14
+
15
+ # push
16
+ FakeWeb.register_uri(:post, "https://my_app_key:my_master_secret@go.urbanairship.com/api/push/", :status => ["200", "OK"])
17
+ FakeWeb.register_uri(:post, "https://my_app_key2:my_master_secret2@go.urbanairship.com/api/push/", :status => ["400", "Bad Request"])
18
+ FakeWeb.register_uri(:post, /bad_key\:my_master_secret\@go\.urbanairship\.com/, :status => ["401", "Unauthorized"])
19
+
20
+ # batch_push
21
+ FakeWeb.register_uri(:post, "https://my_app_key:my_master_secret@go.urbanairship.com/api/push/batch/", :status => ["200", "OK"])
22
+ FakeWeb.register_uri(:post, "https://my_app_key2:my_master_secret2@go.urbanairship.com/api/push/batch/", :status => ["400", "Bad Request"])
23
+
24
+ # broadcast push
25
+ FakeWeb.register_uri(:post, "https://my_app_key:my_master_secret@go.urbanairship.com/api/push/broadcast/", :status => ["200", "OK"])
26
+ FakeWeb.register_uri(:post, "https://my_app_key2:my_master_secret2@go.urbanairship.com/api/push/broadcast/", :status => ["400", "Bad Request"])
27
+
28
+ # delete_scheduled_push
29
+ FakeWeb.register_uri(:delete, /my_app_key\:my_master_secret\@go\.urbanairship.com\/api\/push\/scheduled\/[0-9]+/, :status => ["204", "No Content"])
30
+ FakeWeb.register_uri(:delete, /my_app_key\:my_master_secret\@go\.urbanairship.com\/api\/push\/scheduled\/alias\/.+/, :status => ["204", "No Content"])
31
+ FakeWeb.register_uri(:delete, /bad_key\:my_master_secret\@go\.urbanairship.com\/api\/push\/scheduled\/[0-9]+/, :status => ["401", "Unauthorized"])
32
+
33
+ # feedback
34
+ FakeWeb.register_uri(:get, /my_app_key\:my_master_secret\@go\.urbanairship.com\/api\/device_tokens\/feedback/, :status => ["200", "OK"], :body => "[{\"device_token\":\"token\",\"marked_inactive_on\":\"2010-10-14T19:15:13Z\",\"alias\":\"my_alias\"}]")
35
+ FakeWeb.register_uri(:get, /my_app_key2\:my_master_secret2\@go\.urbanairship.com\/api\/device_tokens\/feedback/, :status => ["500", "Internal Server Error"])
36
+ end
37
+
38
+ describe "configuration" do
39
+ it "enables you to configure the application key" do
40
+ Urbanairship.application_key.should be_nil
41
+ Urbanairship.application_key = "asdf1234"
42
+ Urbanairship.application_key.should == "asdf1234"
43
+ end
44
+
45
+ it "enables you to configure the application secret" do
46
+ Urbanairship.application_secret.should be_nil
47
+ Urbanairship.application_secret = "asdf1234"
48
+ Urbanairship.application_secret.should == "asdf1234"
49
+ end
50
+
51
+ it "enables you to configure the master secret" do
52
+ Urbanairship.master_secret.should be_nil
53
+ Urbanairship.master_secret = "asdf1234"
54
+ Urbanairship.master_secret.should == "asdf1234"
55
+ end
56
+ end
57
+
58
+ describe "::register_device" do
59
+ before(:each) do
60
+ @valid_params = {:alias => 'one'}
61
+ Urbanairship.application_key = "my_app_key"
62
+ Urbanairship.application_secret = "my_app_secret"
63
+ end
64
+
65
+ it "raises an error if call is made without an app key and secret configured" do
66
+ Urbanairship.application_key = nil
67
+ Urbanairship.application_secret = nil
68
+
69
+ lambda {
70
+ Urbanairship.register_device("asdf1234")
71
+ }.should raise_error(RuntimeError, "Must configure application_key, application_secret before making this request.")
72
+ end
73
+
74
+ it "uses app key and secret to sign the request" do
75
+ Urbanairship.register_device("new_device_token")
76
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_app_secret').chomp}"
77
+ end
78
+
79
+ it "takes and sends a device token" do
80
+ Urbanairship.register_device("new_device_token")
81
+ FakeWeb.last_request.path.should == "/api/device_tokens/new_device_token"
82
+ end
83
+
84
+ it "returns true when the device is registered for the first time" do
85
+ Urbanairship.register_device("new_device_token").success?.should == true
86
+ end
87
+
88
+ it "returns true when the device is registered again" do
89
+ Urbanairship.register_device("existing_device_token").success?.should == true
90
+ end
91
+
92
+ it "returns false when the authorization is invalid" do
93
+ Urbanairship.application_key = "bad_key"
94
+ Urbanairship.register_device("new_device_token").success?.should == false
95
+ end
96
+
97
+ it "accepts an alias" do
98
+ Urbanairship.register_device("device_token_one", @valid_params).success?.should == true
99
+ end
100
+
101
+ it "adds alias to the JSON payload" do
102
+ Urbanairship.register_device("device_token_one", @valid_params)
103
+ request_json['alias'].should == "one"
104
+ end
105
+
106
+ it "converts alias param to string" do
107
+ Urbanairship.register_device("device_token_one", :alias => 11)
108
+ request_json['alias'].should be_a_kind_of String
109
+ end
110
+ end
111
+
112
+ describe "::unregister_device" do
113
+ before(:each) do
114
+ Urbanairship.application_key = "my_app_key"
115
+ Urbanairship.application_secret = "my_app_secret"
116
+ end
117
+
118
+ it "raises an error if call is made without an app key and secret configured" do
119
+ Urbanairship.application_key = nil
120
+ Urbanairship.application_secret = nil
121
+
122
+ lambda {
123
+ Urbanairship.unregister_device("asdf1234")
124
+ }.should raise_error(RuntimeError, "Must configure application_key, application_secret before making this request.")
125
+ end
126
+
127
+ it "uses app key and secret to sign the request" do
128
+ Urbanairship.unregister_device("key_to_delete")
129
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_app_secret').chomp}"
130
+ end
131
+
132
+ it "sends the key that needs to be deleted" do
133
+ Urbanairship.unregister_device("key_to_delete")
134
+ FakeWeb.last_request.path.should == "/api/device_tokens/key_to_delete"
135
+ end
136
+
137
+ it "returns true when the device is successfully unregistered" do
138
+ Urbanairship.unregister_device("key_to_delete").success?.should == true
139
+ FakeWeb.last_request.body.should be_nil
140
+ end
141
+
142
+ it "returns false when the authorization is invalid" do
143
+ Urbanairship.application_key = "bad_key"
144
+ Urbanairship.unregister_device("key_to_delete").success?.should == false
145
+ end
146
+ end
147
+
148
+ describe "::delete_scheduled_push" do
149
+ before(:each) do
150
+ Urbanairship.application_key = "my_app_key"
151
+ Urbanairship.master_secret = "my_master_secret"
152
+ end
153
+
154
+ it "raises an error if call is made without an app key and master secret configured" do
155
+ Urbanairship.application_key = nil
156
+ Urbanairship.master_secret = nil
157
+
158
+ lambda {
159
+ Urbanairship.delete_scheduled_push("123456789")
160
+ }.should raise_error(RuntimeError, "Must configure application_key, master_secret before making this request.")
161
+ end
162
+
163
+ it "uses app key and secret to sign the request" do
164
+ Urbanairship.delete_scheduled_push("123456789")
165
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_master_secret').chomp}"
166
+ end
167
+
168
+ it "sends the key that needs to be deleted" do
169
+ Urbanairship.delete_scheduled_push("123456789")
170
+ FakeWeb.last_request.path.should == "/api/push/scheduled/123456789"
171
+ end
172
+
173
+ it "sends the key that needs to be deleted" do
174
+ Urbanairship.delete_scheduled_push(123456789)
175
+ FakeWeb.last_request.path.should == "/api/push/scheduled/123456789"
176
+ end
177
+
178
+ it "sends the alias that needs to be deleted" do
179
+ Urbanairship.delete_scheduled_push(:alias => "alias_to_delete")
180
+ FakeWeb.last_request.path.should == "/api/push/scheduled/alias/alias_to_delete"
181
+ end
182
+
183
+ it "returns true when the push notification is successfully deleted" do
184
+ Urbanairship.delete_scheduled_push("123456789").success?.should == true
185
+ FakeWeb.last_request.body.should be_nil
186
+ end
187
+
188
+ it "returns false when the authorization is invalid" do
189
+ Urbanairship.application_key = "bad_key"
190
+ Urbanairship.delete_scheduled_push("123456789").success?.should == false
191
+ end
192
+ end
193
+
194
+ describe "::push" do
195
+ before(:each) do
196
+ @valid_params = {:device_tokens => ['device_token_one', 'device_token_two'], :aps => {:alert => 'foo'}}
197
+ Urbanairship.application_key = "my_app_key"
198
+ Urbanairship.master_secret = "my_master_secret"
199
+ end
200
+
201
+ it "raises an error if call is made without an app key and master secret configured" do
202
+ Urbanairship.application_key = nil
203
+ Urbanairship.master_secret = nil
204
+
205
+ lambda {
206
+ Urbanairship.push(@valid_params)
207
+ }.should raise_error(RuntimeError, "Must configure application_key, master_secret before making this request.")
208
+ end
209
+
210
+ it "uses app key and secret to sign the request" do
211
+ Urbanairship.push(@valid_params)
212
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_master_secret').chomp}"
213
+ end
214
+
215
+ it "returns true when it successfully pushes a notification" do
216
+ Urbanairship.push(@valid_params).success?.should == true
217
+ end
218
+
219
+ it "returns false when the authorization is invalid" do
220
+ Urbanairship.application_key = "bad_key"
221
+ Urbanairship.push(@valid_params).success?.should == false
222
+ end
223
+
224
+ it "adds schedule_for to the JSON payload" do
225
+ time = Time.parse("Oct 17th, 2010, 8:00 PM UTC")
226
+ Urbanairship.push(@valid_params.merge(:schedule_for => [time]))
227
+ request_json['schedule_for'].should == ['2010-10-17T20:00:00Z']
228
+ end
229
+
230
+ it "only attempts to format schedule_for if it is a time object" do
231
+ Urbanairship.push(@valid_params.merge(:schedule_for => ["2010-10-10 09:09:09 UTC"]))
232
+ request_json['schedule_for'].should == ['2010-10-10T09:09:09Z']
233
+ end
234
+
235
+ it "returns false if urbanairship responds with a non-200 response" do
236
+ Urbanairship.application_key = "my_app_key2"
237
+ Urbanairship.master_secret = "my_master_secret2"
238
+ Urbanairship.push.success?.should == false
239
+ end
240
+ end
241
+
242
+ describe "::batch_push" do
243
+ before(:each) do
244
+ @valid_params = [
245
+ {:device_tokens => ['device_token_one', 'device_token_two'], :aps => {:alert => 'foo'}},
246
+ {:device_tokens => ['device_token_three', 'device_token_four'], :aps => {:alert => 'bar'}}
247
+ ]
248
+ Urbanairship.application_key = "my_app_key"
249
+ Urbanairship.master_secret = "my_master_secret"
250
+ end
251
+
252
+ it "raises an error if call is made without an app key and master secret configured" do
253
+ Urbanairship.application_key = nil
254
+ Urbanairship.master_secret = nil
255
+
256
+ lambda {
257
+ Urbanairship.batch_push(@valid_params)
258
+ }.should raise_error(RuntimeError, "Must configure application_key, master_secret before making this request.")
259
+ end
260
+
261
+ it "uses app key and secret to sign the request" do
262
+ Urbanairship.batch_push(@valid_params)
263
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_master_secret').chomp}"
264
+ end
265
+
266
+ it "returns true when it successfully pushes a notification" do
267
+ Urbanairship.batch_push(@valid_params).success?.should == true
268
+ end
269
+
270
+ it "returns false when the authorization is invalid" do
271
+ Urbanairship.application_key = "bad_key"
272
+ Urbanairship.batch_push(@valid_params).success?.should == false
273
+ end
274
+
275
+ it "adds schedule_for to the JSON payload" do
276
+ time = Time.parse("Oct 17th, 2010, 8:00 PM UTC")
277
+ @valid_params[0].merge!(:schedule_for => [time])
278
+ Urbanairship.batch_push(@valid_params)
279
+ request_json[0]['schedule_for'].should == ['2010-10-17T20:00:00Z']
280
+ end
281
+
282
+ it "accepts strings as schedule_for values" do
283
+ @valid_params[0].merge!(:schedule_for => ["2010-10-10 09:09:09 UTC"])
284
+ Urbanairship.batch_push(@valid_params)
285
+ request_json[0]['schedule_for'].should == ['2010-10-10T09:09:09Z']
286
+ end
287
+
288
+ it "returns false if urbanairship responds with a non-200 response" do
289
+ Urbanairship.application_key = "my_app_key2"
290
+ Urbanairship.master_secret = "my_master_secret2"
291
+ Urbanairship.batch_push.success?.should == false
292
+ end
293
+ end
294
+
295
+ describe "::broadcast_push" do
296
+ before(:each) do
297
+ @valid_params = {:aps => {:alert => 'foo'}}
298
+ Urbanairship.application_key = "my_app_key"
299
+ Urbanairship.master_secret = "my_master_secret"
300
+ end
301
+
302
+ it "raises an error if call is made without an app key and master secret configured" do
303
+ Urbanairship.application_key = nil
304
+ Urbanairship.master_secret = nil
305
+
306
+ lambda {
307
+ Urbanairship.broadcast_push(@valid_params)
308
+ }.should raise_error(RuntimeError, "Must configure application_key, master_secret before making this request.")
309
+ end
310
+
311
+ it "uses app key and secret to sign the request" do
312
+ Urbanairship.broadcast_push(@valid_params)
313
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_master_secret').chomp}"
314
+ end
315
+
316
+ it "returns true when it successfully pushes a notification" do
317
+ Urbanairship.broadcast_push(@valid_params).success?.should == true
318
+ end
319
+
320
+ it "returns false when the authorization is invalid" do
321
+ Urbanairship.application_key = "bad_key"
322
+ Urbanairship.broadcast_push(@valid_params).success?.should == false
323
+ end
324
+
325
+ it "adds schedule_for to the JSON payload" do
326
+ time = Time.parse("Oct 17th, 2010, 8:00 PM UTC")
327
+ @valid_params[:schedule_for] = [time]
328
+ Urbanairship.broadcast_push(@valid_params)
329
+ request_json['schedule_for'].should == ['2010-10-17T20:00:00Z']
330
+ end
331
+
332
+ it "accepts strings as schedule_for values" do
333
+ @valid_params[:schedule_for] = ["2010-10-10 09:09:09 UTC"]
334
+ Urbanairship.broadcast_push(@valid_params)
335
+ request_json['schedule_for'].should == ['2010-10-10T09:09:09Z']
336
+ end
337
+
338
+ it "returns false if urbanairship responds with a non-200 response" do
339
+ Urbanairship.application_key = "my_app_key2"
340
+ Urbanairship.master_secret = "my_master_secret2"
341
+ Urbanairship.broadcast_push.success?.should == false
342
+ end
343
+ end
344
+
345
+ describe "::feedback" do
346
+ before(:each) do
347
+ Urbanairship.application_key = "my_app_key"
348
+ Urbanairship.master_secret = "my_master_secret"
349
+ end
350
+
351
+ it "raises an error if call is made without an app key and master secret configured" do
352
+ Urbanairship.application_key = nil
353
+ Urbanairship.master_secret = nil
354
+
355
+ lambda {
356
+ Urbanairship.feedback(Time.now)
357
+ }.should raise_error(RuntimeError, "Must configure application_key, master_secret before making this request.")
358
+ end
359
+
360
+ it "uses app key and secret to sign the request" do
361
+ Urbanairship.feedback(Time.now)
362
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_master_secret').chomp}"
363
+ end
364
+
365
+ it "encodes the time argument in UTC, ISO 8601 format" do
366
+ time = Time.parse("October 10, 2010, 8:00pm")
367
+ formatted_time = time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
368
+ Urbanairship.feedback(time)
369
+ FakeWeb.last_request.path.should include(formatted_time)
370
+ end
371
+
372
+ it "accepts a string as the time argument" do
373
+ Urbanairship.feedback("Oct 07, 2010 8:00AM UTC")
374
+ FakeWeb.last_request.path.should include("2010-10-07T08:00:00Z")
375
+ end
376
+
377
+ it "returns an array of responses from the feedback API" do
378
+ response = Urbanairship.feedback(Time.now)
379
+ response[0].should include("device_token")
380
+ response[0].should include("marked_inactive_on")
381
+ response[0].should include("alias")
382
+ end
383
+
384
+ it "success? is false when the call doesn't return 200" do
385
+ Urbanairship.application_key = "my_app_key2"
386
+ Urbanairship.master_secret = "my_master_secret2"
387
+ Urbanairship.feedback(Time.now).success?.should == false
388
+ end
389
+ end
390
+
391
+ describe "logging" do
392
+
393
+ before(:each) do
394
+ @logger = mock("logger", :info => true)
395
+ Urbanairship.application_key = "my_app_key"
396
+ Urbanairship.application_secret = "my_app_secret"
397
+ Urbanairship.master_secret = "my_master_secret"
398
+ Urbanairship.logger = @logger
399
+ end
400
+
401
+ it "logs request and response information when registering a device" do
402
+ @logger.should_receive(:info).with(/\/api\/device_tokens\/new_device_token/)
403
+ Urbanairship.register_device('new_device_token')
404
+ end
405
+
406
+ it "logs request and response information when sending push notifications" do
407
+ @logger.should_receive(:info).with(/\/api\/push/)
408
+ Urbanairship.push(:device_tokens => ["device_token"], :aps => {:alert => "foo"})
409
+ end
410
+
411
+ it "logs request and response information when sending batch push notifications" do
412
+ @logger.should_receive(:info).with(/\/api\/push\/batch/)
413
+ Urbanairship.batch_push([:device_tokens => ["device_token"], :aps => {:alert => "foo"}])
414
+ end
415
+
416
+ it "logs request and response information when sending feedback requests" do
417
+ @logger.should_receive(:info).with(/\/api\/device_tokens\/feedback/)
418
+ Urbanairship.feedback(Time.now)
419
+ end
420
+
421
+ it "flushes the logger buffer if it's an ActiveSupport::BufferedLogger (Default Rails logger)" do
422
+ @logger.stub(:flush).and_return("message in the buffer\n")
423
+ @logger.should_receive(:flush)
424
+ Urbanairship.feedback(Time.now)
425
+ end
426
+ end
427
+
428
+ describe "request timeout" do
429
+ before(:each) do
430
+ @logger = mock("logger", :info => true)
431
+ Urbanairship.application_key = "my_app_key"
432
+ Urbanairship.application_secret = "my_app_secret"
433
+ Urbanairship.master_secret = "my_master_secret"
434
+ Urbanairship.logger = @logger
435
+ end
436
+
437
+ it "uses a default request_timeout value of five seconds" do
438
+ Urbanairship::Timer.should_receive(:timeout).with(5.0).and_raise(Timeout::Error)
439
+ @logger.should_receive(:error).with(/Urbanairship request timed out/)
440
+
441
+ Urbanairship.register_device('new_device_token')
442
+ end
443
+
444
+ it "accepts a configured request_timeout value" do
445
+ Urbanairship::Timer.should_receive(:timeout).with(1.23).and_raise(Timeout::Error)
446
+ @logger.should_receive(:error).with(/Urbanairship request timed out/)
447
+
448
+ Urbanairship.request_timeout = 1.23
449
+ Urbanairship.register_device('new_device_token')
450
+ end
451
+ end
452
+ end
453
+
454
+ def request_json
455
+ JSON.parse FakeWeb.last_request.body
456
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sparqcode-urbanairship
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease:
6
+ segments:
7
+ - 2
8
+ - 0
9
+ - 0
10
+ version: 2.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Groupon, Inc.
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-06-14 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: json
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: fakeweb
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :development
61
+ version_requirements: *id003
62
+ description: Urbanairship is a Ruby library for interacting with the Urban Airship (http://urbanairship.com) API. Includes updates from ScoutMob.
63
+ email:
64
+ - rubygems@groupon.com
65
+ executables: []
66
+
67
+ extensions: []
68
+
69
+ extra_rdoc_files: []
70
+
71
+ files:
72
+ - lib/urbanairship/base.rb
73
+ - lib/urbanairship/response.rb
74
+ - lib/urbanairship/service.rb
75
+ - lib/urbanairship.rb
76
+ - README.markdown
77
+ - LICENSE
78
+ - Rakefile
79
+ - spec/response_spec.rb
80
+ - spec/spec_helper.rb
81
+ - spec/urbanairship_spec.rb
82
+ homepage: http://github.com/SPARQCode/urbanairship
83
+ licenses: []
84
+
85
+ post_install_message:
86
+ rdoc_options: []
87
+
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ hash: 59
96
+ segments:
97
+ - 1
98
+ - 8
99
+ - 6
100
+ version: 1.8.6
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: 3
107
+ segments:
108
+ - 0
109
+ version: "0"
110
+ requirements: []
111
+
112
+ rubyforge_project:
113
+ rubygems_version: 1.8.10
114
+ signing_key:
115
+ specification_version: 3
116
+ summary: A Ruby wrapper for the Urban Airship API
117
+ test_files:
118
+ - spec/response_spec.rb
119
+ - spec/spec_helper.rb
120
+ - spec/urbanairship_spec.rb