zaarly-urbanairship 1.1.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.
data/README.markdown ADDED
@@ -0,0 +1,82 @@
1
+ Urbanairship is a Ruby library for interacting with the [Urbanairship API](http://urbanairship.com).
2
+
3
+ Installation
4
+ ============
5
+ gem install urbanairship
6
+
7
+ Configuration
8
+ =============
9
+ ```ruby
10
+ Urbanairship.application_key = 'application-key'
11
+ Urbanairship.application_secret = 'application-secret'
12
+ Urbanairship.master_secret = 'master-secret'
13
+ Urbanairship.logger = Rails.logger
14
+ Urbanairship.request_timeout = 5 # default
15
+ ```
16
+
17
+ Usage
18
+ =====
19
+
20
+ Registering a device token
21
+ --------------------------
22
+ ```ruby
23
+ Urbanairship.register_device 'DEVICE-TOKEN' # => true
24
+ ```
25
+
26
+ Unregistering a device token
27
+ ----------------------------
28
+ ```ruby
29
+ Urbanairship.unregister_device 'DEVICE-TOKEN' # => true
30
+ ```
31
+
32
+ Sending a push notification
33
+ ---------------------------
34
+ ```ruby
35
+ notification = {
36
+ :schedule_for => 1.hour.from_now,
37
+ :device_tokens => ['DEVICE-TOKEN-ONE', 'DEVICE-TOKEN-TWO'],
38
+ :aps => {:alert => 'You have a new message!', :badge => 1}
39
+ }
40
+
41
+ Urbanairship.push notification # => true
42
+ ```
43
+
44
+ Batching push notification sends
45
+ --------------------------------
46
+ ```ruby
47
+ notifications = [
48
+ {
49
+ :schedule_for => 1.hour.from_now,
50
+ :device_tokens => ['DEVICE-TOKEN-ONE', 'DEVICE-TOKEN-TWO'],
51
+ :aps => {:alert => 'You have a new message!', :badge => 1}
52
+ },
53
+ {
54
+ :schedule_for => 3.hours.from_now,
55
+ :device_tokens => ['DEVICE-TOKEN-THREE'],
56
+ :aps => {:alert => 'You have a new message!', :badge => 1}
57
+ }
58
+ ]
59
+
60
+ Urbanairship.batch_push notifications # => true
61
+ ```
62
+
63
+ Polling the feedback API
64
+ ------------------------
65
+ 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 Urbanairship will register that token in their feedback API. Urbanairship will prevent further attempted notification sends to that device, but it's a good practice to periodically poll Urbanairship's feedback API and mark those tokens as inactive in your own system as well.
66
+
67
+ ```ruby
68
+ # find all device tokens deactivated in the past 24 hours
69
+ Urbanairship.feedback 24.hours.ago # =>
70
+ # [
71
+ # {
72
+ # "marked_inactive_on"=>"2011-06-03 22:53:23",
73
+ # "alias"=>nil,
74
+ # "device_token"=>"DEVICE-TOKEN-ONE"
75
+ # },
76
+ # {
77
+ # "marked_inactive_on"=>"2011-06-03 22:53:23",
78
+ # "alias"=>nil,
79
+ # "device_token"=>"DEVICE-TOKEN-TWO"
80
+ # }
81
+ # ]
82
+ ```
data/Rakefile ADDED
@@ -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,99 @@
1
+ require 'json'
2
+ require 'net/https'
3
+ require 'time'
4
+ require 'timeout'
5
+
6
+ module Urbanairship
7
+
8
+ class << self
9
+ attr_accessor :application_key, :application_secret, :master_secret, :logger, :request_timeout
10
+
11
+ def register_device(device_token)
12
+ response = do_request(:put, "/api/device_tokens/#{device_token}", :authenticate_with => :application_secret)
13
+ response && %w(200 201).include?(response.code)
14
+ end
15
+
16
+ def unregister_device(device_token)
17
+ response = do_request(:delete, "/api/device_tokens/#{device_token}", :authenticate_with => :application_secret)
18
+ response && response.code == "204"
19
+ end
20
+
21
+ def push(options = {})
22
+ response = do_request(:post, "/api/push/", :authenticate_with => :master_secret) do |request|
23
+ request.body = parse_push_options(options).to_json
24
+ request.add_field "Content-Type", "application/json"
25
+ end
26
+
27
+ response && response.code == "200"
28
+ end
29
+
30
+ def batch_push(notifications = [])
31
+ response = do_request(:post, "/api/push/batch/", :authenticate_with => :master_secret) do |request|
32
+ request.body = notifications.map{|notification| parse_push_options(notification)}.to_json
33
+ request.add_field "Content-Type", "application/json"
34
+ end
35
+
36
+ response && response.code == "200"
37
+ end
38
+
39
+ def feedback(time)
40
+ response = do_request(:get, "/api/device_tokens/feedback/?since=#{format_time(time)}", :authenticate_with => :master_secret)
41
+ response && response.code == "200" ? JSON.parse(response.body) : false
42
+ end
43
+
44
+ private
45
+
46
+ def do_request(http_method, path, options = {})
47
+ verify_configuration_values(:application_key, options[:authenticate_with])
48
+
49
+ klass = Net::HTTP.const_get(http_method.to_s.capitalize)
50
+
51
+ request = klass.new(path)
52
+ request.basic_auth @application_key, instance_variable_get("@#{options[:authenticate_with]}")
53
+
54
+ yield(request) if block_given?
55
+
56
+ Timeout::timeout(request_timeout) do
57
+ start_time = Time.now
58
+ response = http_client.request(request)
59
+ log_request_and_response(request, response, Time.now - start_time)
60
+ response
61
+ end
62
+ rescue Timeout::Error
63
+ logger.error "Urbanairship request timed out after #{request_timeout} seconds: [#{http_method} #{request.path} #{request.body}]"
64
+ return false
65
+ end
66
+
67
+ def verify_configuration_values(*symbols)
68
+ absent_values = symbols.select{|symbol| instance_variable_get("@#{symbol}").nil? }
69
+ raise("Must configure #{absent_values.join(", ")} before making this request.") unless absent_values.empty?
70
+ end
71
+
72
+ def http_client
73
+ Net::HTTP.new("go.urbanairship.com", 443).tap{|http| http.use_ssl = true}
74
+ end
75
+
76
+ def parse_push_options(hash = {})
77
+ hash[:schedule_for] = hash[:schedule_for].map{|time| format_time(time)} unless hash[:schedule_for].nil?
78
+ hash
79
+ end
80
+
81
+ def log_request_and_response(request, response, time)
82
+ return if logger.nil?
83
+
84
+ time = (time * 1000).to_i
85
+ http_method = request.class.to_s.split('::')[-1]
86
+ logger.info "Urbanairship (#{time}ms): [#{http_method} #{request.path}, #{request.body}], [#{response.code}, #{response.body}]"
87
+ logger.flush if logger.respond_to?(:flush)
88
+ end
89
+
90
+ def format_time(time)
91
+ time = Time.parse(time) if time.is_a?(String)
92
+ time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
93
+ end
94
+
95
+ def request_timeout
96
+ @request_timeout || 5.0
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,4 @@
1
+ require 'base64'
2
+ require 'fakeweb'
3
+
4
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/urbanairship')
@@ -0,0 +1,444 @@
1
+ require 'spec_helper'
2
+
3
+ describe Urbanairship do
4
+
5
+ before(:all) do
6
+ FakeWeb.allow_net_connect = false
7
+
8
+ # register_device
9
+ FakeWeb.register_uri(:put, "https://my_app_key:my_app_secret@go.urbanairship.com/api/device_tokens/new_device_token", :status => ["201", "Created"])
10
+ FakeWeb.register_uri(:put, "https://my_app_key:my_app_secret@go.urbanairship.com/api/device_tokens/existing_device_token", :status => ["200", "OK"])
11
+ FakeWeb.register_uri(:put, /bad_key\:my_app_secret\@go\.urbanairship\.com/, :status => ["401", "Unauthorized"])
12
+
13
+ # unregister_device
14
+ FakeWeb.register_uri(:delete, /my_app_key\:my_app_secret\@go\.urbanairship.com\/api\/device_tokens\/.+/, :status => ["204", "No Content"])
15
+ FakeWeb.register_uri(:delete, /bad_key\:my_app_secret\@go\.urbanairship.com\/api\/device_tokens\/.+/, :status => ["401", "Unauthorized"])
16
+
17
+ # push
18
+ FakeWeb.register_uri(:post, "https://my_app_key:my_master_secret@go.urbanairship.com/api/push/", :status => ["200", "OK"])
19
+ FakeWeb.register_uri(:post, "https://my_app_key2:my_master_secret2@go.urbanairship.com/api/push/", :status => ["400", "Bad Request"])
20
+ FakeWeb.register_uri(:post, /bad_key\:my_master_secret\@go\.urbanairship\.com/, :status => ["401", "Unauthorized"])
21
+
22
+ # batch_push
23
+ FakeWeb.register_uri(:post, "https://my_app_key:my_master_secret@go.urbanairship.com/api/push/batch/", :status => ["200", "OK"])
24
+ FakeWeb.register_uri(:post, "https://my_app_key2:my_master_secret2@go.urbanairship.com/api/push/batch/", :status => ["400", "Bad Request"])
25
+
26
+ # feedback
27
+ 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\"}]")
28
+ FakeWeb.register_uri(:get, /my_app_key2\:my_master_secret2\@go\.urbanairship.com\/api\/device_tokens\/feedback/, :status => ["500", "Internal Server Error"])
29
+ end
30
+
31
+ after(:each) do
32
+ # reset configuration
33
+ Urbanairship.application_key = nil
34
+ Urbanairship.application_secret = nil
35
+ Urbanairship.master_secret = nil
36
+ Urbanairship.logger = nil
37
+
38
+ FakeWeb.instance_variable_set("@last_request", nil)
39
+ end
40
+
41
+ describe "configuration" do
42
+
43
+ it "enables you to configure the application key" do
44
+ Urbanairship.application_key.should be_nil
45
+ Urbanairship.application_key = "asdf1234"
46
+ Urbanairship.application_key.should == "asdf1234"
47
+ end
48
+
49
+ it "enables you to configure the application secret" do
50
+ Urbanairship.application_secret.should be_nil
51
+ Urbanairship.application_secret = "asdf1234"
52
+ Urbanairship.application_secret.should == "asdf1234"
53
+ end
54
+
55
+ it "enables you to configure the master secret" do
56
+ Urbanairship.master_secret.should be_nil
57
+ Urbanairship.master_secret = "asdf1234"
58
+ Urbanairship.master_secret.should == "asdf1234"
59
+ end
60
+
61
+ end
62
+
63
+ describe "registering a device" do
64
+
65
+ before(:each) do
66
+ Urbanairship.application_key = "my_app_key"
67
+ Urbanairship.application_secret = "my_app_secret"
68
+ end
69
+
70
+ it "raises an error if call is made without an app key and secret configured" do
71
+ Urbanairship.application_key = nil
72
+ Urbanairship.application_secret = nil
73
+
74
+ lambda {
75
+ Urbanairship.register_device("asdf1234")
76
+ }.should raise_error(RuntimeError, "Must configure application_key, application_secret before making this request.")
77
+ end
78
+
79
+ it "uses app key and secret to sign the request" do
80
+ Urbanairship.register_device("new_device_token")
81
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_app_secret').chomp}"
82
+ end
83
+
84
+ it "takes and sends a device token" do
85
+ Urbanairship.register_device("new_device_token")
86
+ FakeWeb.last_request.path.should == "/api/device_tokens/new_device_token"
87
+ end
88
+
89
+ it "returns true when the device is registered for the first time" do
90
+ Urbanairship.register_device("new_device_token").should == true
91
+ end
92
+
93
+ it "returns true when the device is registered again" do
94
+ Urbanairship.register_device("existing_device_token").should == true
95
+ end
96
+
97
+ it "returns false when the authorization is invalid" do
98
+ Urbanairship.application_key = "bad_key"
99
+ Urbanairship.register_device("new_device_token").should == false
100
+ end
101
+
102
+ # TODO:
103
+ # it "accepts additional parameters (EXPAND THIS)"
104
+
105
+ end
106
+
107
+ describe "unregistering a device" do
108
+ before(:each) do
109
+ Urbanairship.application_key = "my_app_key"
110
+ Urbanairship.application_secret = "my_app_secret"
111
+ end
112
+
113
+ it "raises an error if call is made without an app key and secret configured" do
114
+ Urbanairship.application_key = nil
115
+ Urbanairship.application_secret = nil
116
+
117
+ lambda {
118
+ Urbanairship.unregister_device("asdf1234")
119
+ }.should raise_error(RuntimeError, "Must configure application_key, application_secret before making this request.")
120
+ end
121
+
122
+ it "uses app key and secret to sign the request" do
123
+ Urbanairship.unregister_device("key_to_delete")
124
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_app_secret').chomp}"
125
+ end
126
+
127
+ it "sends the key that needs to be deleted" do
128
+ Urbanairship.unregister_device("key_to_delete")
129
+ FakeWeb.last_request.path.should == "/api/device_tokens/key_to_delete"
130
+ end
131
+
132
+ it "returns true when the device is successfully unregistered" do
133
+ Urbanairship.unregister_device("key_to_delete").should == true
134
+ FakeWeb.last_request.body.should be_nil
135
+ end
136
+
137
+ it "returns false when the authorization is invalid" do
138
+ Urbanairship.application_key = "bad_key"
139
+ Urbanairship.unregister_device("key_to_delete").should == false
140
+ end
141
+
142
+ end
143
+
144
+ describe "sending multiple push notifications" do
145
+
146
+ before(:each) do
147
+ @valid_params = {:device_tokens => ['device_token_one', 'device_token_two'], :aps => {:alert => 'foo'}}
148
+ Urbanairship.application_key = "my_app_key"
149
+ Urbanairship.master_secret = "my_master_secret"
150
+ end
151
+
152
+ it "raises an error if call is made without an app key and master secret configured" do
153
+ Urbanairship.application_key = nil
154
+ Urbanairship.master_secret = nil
155
+
156
+ lambda {
157
+ Urbanairship.push(@valid_params)
158
+ }.should raise_error(RuntimeError, "Must configure application_key, master_secret before making this request.")
159
+ end
160
+
161
+ it "uses app key and secret to sign the request" do
162
+ Urbanairship.push(@valid_params)
163
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_master_secret').chomp}"
164
+ end
165
+
166
+ it "returns true when it successfully pushes a notification" do
167
+ Urbanairship.push(@valid_params).should == true
168
+ end
169
+
170
+ it "returns false when the authorization is invalid" do
171
+ Urbanairship.application_key = "bad_key"
172
+ Urbanairship.push(@valid_params).should == false
173
+ end
174
+
175
+ it "sets the content-type header to application/json" do
176
+ Urbanairship.push(@valid_params)
177
+ FakeWeb.last_request['content-type'].should == 'application/json'
178
+ end
179
+
180
+ it "adds device_tokens to the JSON payload" do
181
+ Urbanairship.push(@valid_params.merge(:device_tokens => ["one", "two"]))
182
+ request_json['device_tokens'].should == ["one", "two"]
183
+ end
184
+
185
+ it "adds aliases to the JSON payload" do
186
+ Urbanairship.push(@valid_params.merge(:aliases => ["one", "two"]))
187
+ request_json['aliases'].should == ["one", "two"]
188
+ end
189
+
190
+ it "adds tags to the JSON payload" do
191
+ Urbanairship.push(@valid_params.merge(:tags => ["one", "two"]))
192
+ request_json['tags'].should == ["one", "two"]
193
+ end
194
+
195
+ it "adds schedule_for to the JSON payload" do
196
+ time = Time.parse("Oct 17th, 2010, 8:00 PM UTC")
197
+ Urbanairship.push(@valid_params.merge(:schedule_for => [time]))
198
+ request_json['schedule_for'].should == ['2010-10-17T20:00:00Z']
199
+ end
200
+
201
+ it "only attempts to format schedule_for if it is a time object" do
202
+ Urbanairship.push(@valid_params.merge(:schedule_for => ["2010-10-10 09:09:09 UTC"]))
203
+ request_json['schedule_for'].should == ['2010-10-10T09:09:09Z']
204
+ end
205
+
206
+ it "adds exclude_tokens to the JSON payload" do
207
+ Urbanairship.push(@valid_params.merge(:exclude_tokens => ["one", "two"]))
208
+ request_json['exclude_tokens'].should == ["one", "two"]
209
+ end
210
+
211
+ it "adds aps parameters to the JSON payload" do
212
+ Urbanairship.push(@valid_params.merge(:aps => {:badge => 10, :alert => "Hi!", :sound => "cat.caf"}))
213
+ request_json['aps'].should == {'badge' => 10, 'alert' => 'Hi!', 'sound' => 'cat.caf'}
214
+ end
215
+
216
+ it "includes extra parameters from the JSON payload" do
217
+ Urbanairship.push(@valid_params.merge(:foo => 'bar'))
218
+ request_json['foo'].should == 'bar'
219
+ end
220
+
221
+ it "returns false if urbanairship responds with a non-200 response" do
222
+ Urbanairship.application_key = "my_app_key2"
223
+ Urbanairship.master_secret = "my_master_secret2"
224
+ Urbanairship.push.should == false
225
+ end
226
+
227
+ end
228
+
229
+ describe "sending batch push notifications" do
230
+
231
+ before(:each) do
232
+ @valid_params = [
233
+ {:device_tokens => ['device_token_one', 'device_token_two'], :aps => {:alert => 'foo'}},
234
+ {:device_tokens => ['device_token_three', 'device_token_four'], :aps => {:alert => 'bar'}}
235
+ ]
236
+ Urbanairship.application_key = "my_app_key"
237
+ Urbanairship.master_secret = "my_master_secret"
238
+ end
239
+
240
+ it "raises an error if call is made without an app key and master secret configured" do
241
+ Urbanairship.application_key = nil
242
+ Urbanairship.master_secret = nil
243
+
244
+ lambda {
245
+ Urbanairship.batch_push(@valid_params)
246
+ }.should raise_error(RuntimeError, "Must configure application_key, master_secret before making this request.")
247
+ end
248
+
249
+ it "uses app key and secret to sign the request" do
250
+ Urbanairship.batch_push(@valid_params)
251
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_master_secret').chomp}"
252
+ end
253
+
254
+ it "returns true when it successfully pushes a notification" do
255
+ Urbanairship.batch_push(@valid_params).should == true
256
+ end
257
+
258
+ it "returns false when the authorization is invalid" do
259
+ Urbanairship.application_key = "bad_key"
260
+ Urbanairship.batch_push(@valid_params).should == false
261
+ end
262
+
263
+ it "sets the content-type header to application/json" do
264
+ Urbanairship.batch_push(@valid_params)
265
+ FakeWeb.last_request['content-type'].should == 'application/json'
266
+ end
267
+
268
+ it "adds device_tokens to the JSON payload" do
269
+ @valid_params[0].merge!(:device_tokens => ["one", "two"])
270
+ Urbanairship.batch_push(@valid_params)
271
+ request_json[0]['device_tokens'].should == ["one", "two"]
272
+ end
273
+
274
+ it "adds aliases to the JSON payload" do
275
+ @valid_params[0].merge!(:aliases => ["one", "two"])
276
+ Urbanairship.batch_push(@valid_params)
277
+ request_json[0]['aliases'].should == ["one", "two"]
278
+ end
279
+
280
+ it "adds tags to the JSON payload" do
281
+ @valid_params[0].merge!(:tags => ["one", "two"])
282
+ Urbanairship.batch_push(@valid_params)
283
+ request_json[0]['tags'].should == ["one", "two"]
284
+ end
285
+
286
+ it "adds schedule_for to the JSON payload" do
287
+ time = Time.parse("Oct 17th, 2010, 8:00 PM UTC")
288
+ @valid_params[0].merge!(:schedule_for => [time])
289
+ Urbanairship.batch_push(@valid_params)
290
+ request_json[0]['schedule_for'].should == ['2010-10-17T20:00:00Z']
291
+ end
292
+
293
+ it "accepts strings as schedule_for values" do
294
+ @valid_params[0].merge!(:schedule_for => ["2010-10-10 09:09:09 UTC"])
295
+ Urbanairship.batch_push(@valid_params)
296
+ request_json[0]['schedule_for'].should == ['2010-10-10T09:09:09Z']
297
+ end
298
+
299
+ it "adds exclude_tokens to the JSON payload" do
300
+ @valid_params[0].merge!(:exclude_tokens => ["one", "two"])
301
+ Urbanairship.batch_push(@valid_params)
302
+ request_json[0]['exclude_tokens'].should == ["one", "two"]
303
+ end
304
+
305
+ it "adds aps parameters to the JSON payload" do
306
+ @valid_params[0].merge!(:aps => {:badge => 10, :alert => "Hi!", :sound => "cat.caf"})
307
+ Urbanairship.batch_push(@valid_params)
308
+ request_json[0]['aps'].should == {'badge' => 10, 'alert' => 'Hi!', 'sound' => 'cat.caf'}
309
+ end
310
+
311
+ it "includes extra parameters from the JSON payload" do
312
+ @valid_params[0].merge!(:foo => 'bar')
313
+ Urbanairship.batch_push(@valid_params)
314
+ request_json[0]['foo'].should == 'bar'
315
+ end
316
+
317
+ it "returns false if urbanairship responds with a non-200 response" do
318
+ Urbanairship.application_key = "my_app_key2"
319
+ Urbanairship.master_secret = "my_master_secret2"
320
+ Urbanairship.batch_push.should == false
321
+ end
322
+
323
+ end
324
+
325
+ describe "feedback service" do
326
+
327
+ before(:each) do
328
+ Urbanairship.application_key = "my_app_key"
329
+ Urbanairship.master_secret = "my_master_secret"
330
+ end
331
+
332
+ it "raises an error if call is made without an app key and master secret configured" do
333
+ Urbanairship.application_key = nil
334
+ Urbanairship.master_secret = nil
335
+
336
+ lambda {
337
+ Urbanairship.feedback(Time.now)
338
+ }.should raise_error(RuntimeError, "Must configure application_key, master_secret before making this request.")
339
+ end
340
+
341
+ it "uses app key and secret to sign the request" do
342
+ Urbanairship.feedback(Time.now)
343
+ FakeWeb.last_request['authorization'].should == "Basic #{Base64::encode64('my_app_key:my_master_secret').chomp}"
344
+ end
345
+
346
+ it "encodes the time argument in UTC, ISO 8601 format" do
347
+ time = Time.parse("October 10, 2010, 8:00pm")
348
+ formatted_time = time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
349
+ Urbanairship.feedback(time)
350
+ FakeWeb.last_request.path.should include(formatted_time)
351
+ end
352
+
353
+ it "accepts a string as the time argument" do
354
+ Urbanairship.feedback("Oct 07, 2010 8:00AM UTC")
355
+ FakeWeb.last_request.path.should include("2010-10-07T08:00:00Z")
356
+ end
357
+
358
+ it "returns an array of responses from the feedback API" do
359
+ response = Urbanairship.feedback(Time.now)
360
+ response.class.should == Array
361
+ response[0].keys.should include("device_token")
362
+ response[0].keys.should include("marked_inactive_on")
363
+ response[0].keys.should include("alias")
364
+ end
365
+
366
+ it "returns false and doesn't parse JSON when the call doesn't return 200" do
367
+ Urbanairship.application_key = "my_app_key2"
368
+ Urbanairship.master_secret = "my_master_secret2"
369
+ JSON.should_not_receive(:parse)
370
+ Urbanairship.feedback(Time.now).should == false
371
+ end
372
+
373
+ end
374
+
375
+ describe "logging" do
376
+
377
+ before(:each) do
378
+ @logger = mock("logger", :info => true)
379
+ Urbanairship.application_key = "my_app_key"
380
+ Urbanairship.application_secret = "my_app_secret"
381
+ Urbanairship.master_secret = "my_master_secret"
382
+ Urbanairship.logger = @logger
383
+ end
384
+
385
+ it "logs request and response information when registering a device" do
386
+ @logger.should_receive(:info).with(/\/api\/device_tokens\/new_device_token/)
387
+ Urbanairship.register_device('new_device_token')
388
+ end
389
+
390
+ it "logs request and response information when sending push notifications" do
391
+ @logger.should_receive(:info).with(/\/api\/push/)
392
+ Urbanairship.push(:device_tokens => ["device_token"], :aps => {:alert => "foo"})
393
+ end
394
+
395
+ it "logs request and response information when sending batch push notifications" do
396
+ @logger.should_receive(:info).with(/\/api\/push\/batch/)
397
+ Urbanairship.batch_push([:device_tokens => ["device_token"], :aps => {:alert => "foo"}])
398
+ end
399
+
400
+ it "logs request and response information when sending feedback requests" do
401
+ @logger.should_receive(:info).with(/\/api\/device_tokens\/feedback/)
402
+ Urbanairship.feedback(Time.now)
403
+ end
404
+
405
+ it "flushes the logger buffer if it's an ActiveSupport::BufferedLogger (Default Rails logger)" do
406
+ @logger.stub(:flush).and_return("message in the buffer\n")
407
+ @logger.should_receive(:flush)
408
+ Urbanairship.feedback(Time.now)
409
+ end
410
+
411
+ end
412
+
413
+ describe "request timeout" do
414
+ before(:each) do
415
+ @logger = mock("logger", :info => true)
416
+ Urbanairship.application_key = "my_app_key"
417
+ Urbanairship.application_secret = "my_app_secret"
418
+ Urbanairship.master_secret = "my_master_secret"
419
+ Urbanairship.logger = @logger
420
+ end
421
+
422
+ it "uses a default request_timeout value of five seconds" do
423
+ # SystemTimer.should_receive(:timeout_after).with(5.0).and_raise(Timeout::Error)
424
+ Timeout.should_receive(:timeout).with(5.0).and_raise(Timeout::Error)
425
+ @logger.should_receive(:error).with(/Urbanairship request timed out/)
426
+
427
+ Urbanairship.register_device('new_device_token')
428
+ end
429
+
430
+ it "accepts a configured request_timeout value" do
431
+ # SystemTimer.should_receive(:timeout_after).with(1.23).and_raise(Timeout::Error)
432
+ Timeout.should_receive(:timeout).with(1.23).and_raise(Timeout::Error)
433
+ @logger.should_receive(:error).with(/Urbanairship request timed out/)
434
+
435
+ Urbanairship.request_timeout = 1.23
436
+ Urbanairship.register_device('new_device_token')
437
+ end
438
+ end
439
+
440
+ end
441
+
442
+ def request_json
443
+ JSON.parse FakeWeb.last_request.body
444
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zaarly-urbanairship
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Groupon, Inc.
9
+ - Zaarly, Inc.
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-07-22 00:00:00 -07:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: json
19
+ prerelease: false
20
+ requirement: &id001 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: "0"
26
+ type: :runtime
27
+ version_requirements: *id001
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec
30
+ prerelease: false
31
+ requirement: &id002 !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: "0"
37
+ type: :development
38
+ version_requirements: *id002
39
+ - !ruby/object:Gem::Dependency
40
+ name: fakeweb
41
+ prerelease: false
42
+ requirement: &id003 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id003
50
+ description: Urbanairship is a Ruby library for interacting with the Urbanairship (http://urbanairship.com) API.
51
+ email:
52
+ - rubygems@groupon.com
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ extra_rdoc_files: []
58
+
59
+ files:
60
+ - README.markdown
61
+ - LICENSE
62
+ - Rakefile
63
+ - lib/urbanairship.rb
64
+ - spec/spec_helper.rb
65
+ - spec/urbanairship_spec.rb
66
+ has_rdoc: true
67
+ homepage: http://github.com/zaarly/urbanairship
68
+ licenses: []
69
+
70
+ post_install_message:
71
+ rdoc_options: []
72
+
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ~>
79
+ - !ruby/object:Gem::Version
80
+ version: 1.9.2
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: "0"
87
+ requirements: []
88
+
89
+ rubyforge_project:
90
+ rubygems_version: 1.6.2
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: A Ruby wrapper for the Urbanairship API
94
+ test_files:
95
+ - spec/spec_helper.rb
96
+ - spec/urbanairship_spec.rb