zaarly-urbanairship 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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