rhoconnect 3.0.4 → 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/doc/push.txt CHANGED
@@ -44,9 +44,17 @@ To setup your RhoConnect application for Android push, you will need to update `
44
44
  :redis: localhost:6379
45
45
  :syncserver: http://localhost:9292/application/
46
46
  :licensefile: settings/license.key
47
+ :c2dm_username: username
48
+ :c2dm_passwd: passwd
47
49
  :authtoken: authtoken
48
50
 
49
- Replace `:authtoken:` value with actual authentication token. This token MUST be related to the role-based google account registered for your application. See [the rhodes push instructions](/rhodes/device-caps#push-notifications) for more details. To retrieve this token, use sample script [c2dm.rb](http://github.com/rhomobile/rhodes/blob/master/bin/c2dm.rb). Uncomment last two lines and put your google account specific data, then run it. It will print token to stdout.
51
+ In order to push messages to the Android device, your server needs to obtain Google's C2DM authentication token associated with the trusted google account.
52
+ For this purpose, you can specify C2DM Google account's username/password combo via the `:c2dm_username` and `:c2dm_passwd` settings.
53
+ At run-time, the system will use these credentials to obtain the C2DM token and store it in the Redis for the future use.
54
+ Once the token is expired, the system will automatically connect to the Google C2DM service to renew the token.
55
+
56
+ Alternatively, you can use `:authtoken` setting to specify the pre-defined authentication token. This token MUST be related to the role-based google account registered for your application. See [the rhodes push instructions](/rhodes/device-caps#push-notifications) for more details. To retrieve this token, use sample script [c2dm.rb](http://github.com/rhomobile/rhodes/blob/master/bin/c2dm.rb). Uncomment last two lines and put your google account specific data, then run it. It will print token to stdout.
57
+ However, this approach will not allow the server to connect to the C2DM service and renew the token once it is expired.
50
58
 
51
59
  For those who interested in what this token means, the description is [here](http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html).
52
60
 
data/doc/rails-plugin.txt CHANGED
@@ -1,9 +1,9 @@
1
1
  rhoconnect-rb
2
2
  ===
3
3
 
4
- A ruby library for the [Rhoconnect](http://rhomobile.com/products/rhoconnect) App Integration Server.
4
+ A ruby library for the [RhoConnect](http://rhomobile.com/products/rhoconnect) App Integration Server.
5
5
 
6
- Using rhoconnect-rb, your application's model data will transparently synchronize with a mobile application built on the [Rhodes framework](http://rhomobile.com/products/rhodes), or any of the available [Rhoconnect clients](http://rhomobile.com/products/rhoconnect/). This client includes built-in support for [ActiveRecord](http://ar.rubyonrails.org/) and [DataMapper](http://datamapper.org/) models.
6
+ Using rhoconnect-rb, your application's model data will transparently synchronize with a mobile application built on the [Rhodes framework](http://rhomobile.com/products/rhodes), or any of the available [RhoConnect clients](http://rhomobile.com/products/rhoconnect/). This client includes built-in support for [ActiveRecord](http://ar.rubyonrails.org/) and [DataMapper](http://datamapper.org/) models.
7
7
 
8
8
  ## Getting started
9
9
 
@@ -68,7 +68,7 @@ For more information about Rhoconnect partitions, please refer to the [Rhoconnec
68
68
  end
69
69
 
70
70
  def self.rhoconnect_query(partition)
71
- Product.where(:user_id => partition)
71
+ Product.includes(:user).where("users.username = ?",partition)
72
72
  end
73
73
  end
74
74
 
@@ -76,16 +76,15 @@ In this example, `self.rhoconnect_query` returns a list of products where the pa
76
76
 
77
77
  ## Configuration and Authentication
78
78
 
79
- Configure Rhoconnect in an initializer like `config/initializers/rhoconnect.rb` (for Rails), or directly in your application (i.e. Sinatra). Here you will setup the rhoconnect uri (the location of your rhoconnect instance), and app\_endpoint (the location of your ruby app):
79
+ ### Running RhoConnect Manually
80
80
 
81
+ Configure RhoConnect in an initializer like `config/initializers/rhoconnect.rb` (for Rails), or directly in your application (i.e. Sinatra). Here you will setup the rhoconnect uri (the location of your RhoConnect instance), the api\_token (see [rhoconnect:get_token](http://docs.rhomobile.com/rhoconnect/command-line#rake-tasks) rake task), and app\_endpoint (the location of your ruby app):
81
82
 
82
- # Use rhoconnect:get_token to get the token value.
83
-
84
83
  config.uri = "http://myrhoconnect.com"
85
84
  config.token = "secrettoken"
86
85
  config.app_endpoint = "http://myapp.heroku.com"
87
86
 
88
- If `app_endpoint` is defined, your Rhoconnect instance will be configured to query data from the endpoint using the rhoconnect_query method in your model. For example, if your `app_endpoint` is defined as "http://myapp.heroku.com", Rhoconnect will query data with:
87
+ If `app_endpoint` is defined, your Rhoconnect instance will be configured to query data from the endpoint using the rhoconnect_query method in your model. For example, if your `app_endpoint` is defined as "http://myapp.heroku.com", RhoConnect will query data with:
89
88
 
90
89
  POST http://myapp.heroku.com/rhoconnect/query
91
90
 
@@ -109,12 +108,11 @@ Example with authentication:
109
108
  }
110
109
  end
111
110
 
112
- Example using the [RhoConnect Heroku Addon](/rhoconnect/heroku-addon):
111
+ ### Using the [RhoConnect Heroku Addon](http://docs.rhomobile.com/rhoconnect/heroku-addon)
113
112
 
114
- If you're using the [RhoConnect Heroku Addon](/rhoconnect/heroku-addon), then you can omit the config.uri (it is added for you):
113
+ If you're using the [RhoConnect Heroku Addon](http://docs.rhomobile.com/rhoconnect/heroku-addon), then you can omit the config.uri and config.token (they are managed for you):
115
114
 
116
115
  Rhoconnect.configure do |config|
117
- config.token = "secrettoken"
118
116
  config.authenticate = lambda { |credentials|
119
117
  User.authenticate(credentials[:login], credentials[:password])
120
118
  }
data/doc/settings.txt CHANGED
@@ -23,6 +23,27 @@ In addition, the following settings can be specified:
23
23
  as a default sync poll interval.
24
24
  :bulk_data_poll_interval: <secs> - poll interval setting for bulk data sync
25
25
 
26
+ :c2dm_username: <google's email>
27
+ :c2dm_passwd: <passwd>
28
+ :authtoken: <authtoken> - these settings are used specifically for Android push notifications.
29
+ In order to push messages to the Android device, your server needs to
30
+ obtain Google's C2DM authentication token associated with the trusted google account.
31
+ For this purpose, you can specify C2DM Google account's username/password combo
32
+ via the `:c2dm_username` and `:c2dm_passwd` settings.
33
+ At run-time, the system will use these credentials to obtain
34
+ the C2DM token and store it in the Redis for the future use.
35
+ Once the token is expired, the system will automatically
36
+ connect to the Google C2DM service to renew the token.
37
+
38
+ Alternatively, you can use `:authtoken` setting to specify
39
+ the pre-defined authentication token. This token MUST be related to the role-based google account
40
+ registered for your application. See [the rhodes push instructions](/rhodes/device-caps#push-notifications)
41
+ for more details. To retrieve this token, use sample script [c2dm.rb](http://github.com/rhomobile/rhodes/blob/master/bin/c2dm.rb).
42
+ Uncomment last two lines and put your google account specific data, then run it.
43
+ It will print token to stdout.
44
+ This approach, however, will not allow the server to connect
45
+ to the C2DM service and renew the token once it is expired.
46
+
26
47
  ## Source settings
27
48
 
28
49
  All source-specific settings are specified under the corresponding source name entry in the `:sources` category.
@@ -8,7 +8,7 @@ module Constants
8
8
  "libaprutil1-dev",
9
9
  "dtach"]
10
10
 
11
- REDIS = "redis-2.2.14"
11
+ REDIS = "redis-2.4.1"
12
12
  SQLITE3 = "sqlite-autoconf-3070701"
13
13
  RUBY = "ruby-enterprise-1.8.7-2011.03"
14
14
  Nginx = "nginx-1.0.6"
@@ -0,0 +1,5 @@
1
+ desc 'Creates and uploads apt and rpm repos.'
2
+ task "build:repos" => ['build:deb', 'build:rpm'] do
3
+ # Run repos.rb
4
+ ruby "installer/utils/package_upload/repos.rb"
5
+ end #build:repos
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'find'
4
+
5
+ # CONSTANTS
6
+
7
+ PKG_DIR = '/packages'
8
+ START_DIR = PKG_DIR
9
+ BUCKET = 'rhoconnect'
10
+
11
+ def cmd(cmd)
12
+ puts cmd unless @raked
13
+ system(cmd)
14
+ end #cmd
15
+
16
+ def prepare_destination
17
+ # Prompt to remove the /deb directory if it exists
18
+ if File.directory?("#{PKG_DIR}/deb")
19
+ if !@raked
20
+ puts "Remove #{PKG_DIR}/deb?"
21
+ remove = STDIN.gets.strip.downcase.chars.first
22
+ else
23
+ remove = 'y'
24
+ end #if
25
+ if remove == 'y'
26
+ cmd "sudo rm -rf #{PKG_DIR}/deb"
27
+ end #if
28
+ end #if
29
+
30
+ # Create deb directory if it does not already exist
31
+ cmd "sudo mkdir -p #{PKG_DIR}/deb" unless File.directory?("#{PKG_DIR}/deb")
32
+
33
+ # Create configuration file "ditributions" in deb directory
34
+ filename = "#{PKG_DIR}/deb/conf/"
35
+ cmd "sudo mkdir -p #{filename}"
36
+ distributions = "Origin: Rhomobile, Inc.\n" +
37
+ "Label: Rhomobile, Inc.\n" +
38
+ "Codename: rhoconnect\n" +
39
+ "Architectures: i386 amd64\n" +
40
+ "Components: main\n" +
41
+ "Description: Rhoconnect APT Repository"
42
+ cmd "touch #{filename}/distributions"
43
+
44
+ # Write distributions string to corresponding file
45
+ dist_file = File.new("#{filename}/distributions", "w")
46
+ dist_file.write(distributions)
47
+ dist_file.close
48
+
49
+ # Create rpm directory if it does not already exist
50
+ cmd "sudo mkdir -p #{PKG_DIR}/rpm" unless File.directory?("#{PKG_DIR}/rpm")
51
+
52
+ end #prepare_destination
53
+
54
+ def copy_files
55
+ # Copy the packages to their respective directory
56
+ Find.find('./pkg') do |file|
57
+ if !FileTest.directory?(file)
58
+ dest_dir = File.extname(file)
59
+ #get rid of '.' before extension name
60
+ dest_dir[0] = ''
61
+ if dest_dir == 'deb' || dest_dir == 'rpm'
62
+ if dest_dir == 'deb'
63
+ @deb_pkg = File.basename(file)
64
+ end #if
65
+ file_path = File.expand_path(file)
66
+ cmd "sudo cp -rf #{file_path} #{PKG_DIR}/#{dest_dir}"
67
+ end #if
68
+ end #if
69
+ end #do
70
+ end #copy_files
71
+
72
+ @raked = true
73
+
74
+ prepare_destination
75
+
76
+ copy_files
77
+
78
+ # REPOIFY!
79
+ cmd "sudo reprepro -b #{PKG_DIR}/deb includedeb rhoconnect #{PKG_DIR}/deb/#{@deb_pkg}"
80
+ cmd "sudo createrepo #{PKG_DIR}/rpm"
81
+
82
+ # Call S3 upload script
83
+ cmd "ruby ./installer/utils/package_upload/s3_upload.rb #{START_DIR} #{BUCKET} #{@raked}"
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'aws/s3'
5
+ require 'find'
6
+
7
+ # CONSTANTS
8
+
9
+ ACCESS_KEY_FILE = '/home/vagrant/.s3/keys'
10
+ START_DIR = ARGV[0]
11
+ BUCKET = ARGV[1]
12
+
13
+ # METHODS
14
+
15
+ def check_params
16
+ if ARGV.size != 2 && ARGV.size != 3
17
+ puts "Wrong number of arguments (#{ARGV.size} for 2)"
18
+ exit
19
+ end #if
20
+
21
+ if ARGV.size == 3 && ARGV[2] == "true"
22
+ @raked = true
23
+ else
24
+ @raked = false
25
+ end #if
26
+
27
+ if !FileTest.directory?(START_DIR)
28
+ puts "#{START_DIR} is not a directory."
29
+ exit
30
+ end #if
31
+
32
+ begin
33
+ found = AWS::S3::Bucket.find(BUCKET)
34
+ puts "#{BUCKET} is a valid bucket :)" unless @raked
35
+ rescue
36
+ puts "#{BUCKET} is not a valid bucket."
37
+ exit
38
+ end
39
+ end #check_params
40
+
41
+ def get_keys
42
+ lines = IO.readlines ACCESS_KEY_FILE
43
+ @access_key = lines.first.strip
44
+ @secret_access_key = lines.last.strip
45
+ end #get_keys
46
+
47
+ def establish_s3_connection
48
+ get_keys
49
+ @s3_connection = AWS::S3::Base.establish_connection!(
50
+ :access_key_id => @access_key,
51
+ :secret_access_key => @secret_access_key
52
+ )
53
+ end #establish_s3_connection
54
+
55
+ def upload(file)
56
+ puts "Uploading #{file} to bucket #{BUCKET}..." unless @raked
57
+ # Upload the given file
58
+ AWS::S3::S3Object.store( file,
59
+ open( file ),
60
+ BUCKET,
61
+ :access => :public_read )
62
+
63
+ # display the URL of the file just uploaded
64
+ puts AWS::S3::S3Object.url_for((file), BUCKET)[/[^?]+/]
65
+ puts unless @raked
66
+ end #upload
67
+
68
+ def upload_files
69
+ # Get list of files do be added
70
+ number_of_files = 0
71
+ paths = []
72
+ Find.find(START_DIR) do |path|
73
+ if FileTest.directory?(path)
74
+ next
75
+ else
76
+ paths.push(path)
77
+ puts path unless @raked
78
+ number_of_files += 1
79
+ end
80
+ end
81
+
82
+ if !@raked
83
+ puts "Would you like to upload these #{number_of_files} files to the #{BUCKET} bucket? (y/n)"
84
+ choice = STDIN.gets.strip.downcase
85
+ else
86
+ choice = 'y'
87
+ end #if
88
+
89
+ if choice == 'y'
90
+ puts "Uploading #{number_of_files} files."
91
+ paths.each do |path|
92
+ upload path
93
+ end #do
94
+ puts "#{number_of_files} files uploaded."
95
+ else
96
+ puts "Exiting."
97
+ end #if
98
+ end #upload_files
99
+
100
+ establish_s3_connection
101
+
102
+ check_params
103
+
104
+ upload_files
@@ -99,7 +99,7 @@ section "uninstall"
99
99
  Push "PATH"
100
100
  Push "R"
101
101
  Push "HKLM"
102
- Push "$INSTDIR\redis-2.2.14"
102
+ Push "$INSTDIR\redis-2.4.1"
103
103
  Call un.EnvVarUpdate
104
104
  Pop $R0
105
105
 
@@ -170,25 +170,25 @@ Section "Redis" redisSection
170
170
 
171
171
  SetOutPath $INSTDIR
172
172
 
173
- File /r "redis-2.2.14"
173
+ File /r "redis-2.4.1"
174
174
 
175
175
  ;add to path here
176
176
 
177
177
  Push "PATH"
178
178
  Push "P"
179
179
  Push "HKLM"
180
- Push "$INSTDIR\redis-2.2.14"
180
+ Push "$INSTDIR\redis-2.4.1"
181
181
  Call EnvVarUpdate
182
182
  Pop $R0
183
183
 
184
184
  Push "REDIS_HOME"
185
185
  Push "P"
186
186
  Push "HKLM"
187
- Push "$INSTDIR\redis-2.2.14"
187
+ Push "$INSTDIR\redis-2.4.1"
188
188
  Call EnvVarUpdate
189
189
  Pop $R0
190
190
 
191
- ExecWait '$INSTDIR\redis-2.2.14\redis-service.exe install' $0
191
+ ExecWait '$INSTDIR\redis-2.4.1\redis-service.exe install' $0
192
192
  StrCmp $0 "0" continue wrong
193
193
 
194
194
  wrong:
@@ -1,6 +1,13 @@
1
1
  Server.api :clientregister, :application, :post do |params,user,server|
2
2
  server.catch_all do
3
- server.current_client.update_fields(params)
3
+ current_client = server.current_client
4
+ if current_client.nil? and server.current_user and server.current_app
5
+ fields = {:user_id => server.current_user.id}
6
+ fields[:id] = params[:client_id].to_s
7
+ fields[:app_id] = server.current_app.id
8
+ current_client = Client.create(fields)
9
+ end
10
+ current_client.update_fields(params)
4
11
  server.status 200
5
12
  end
6
- end
13
+ end
@@ -17,7 +17,7 @@ module Rhoconnect
17
17
 
18
18
  def self.create(fields,params={})
19
19
  Rhoconnect.license.check_and_use_seat
20
- fields[:id] = get_random_uuid
20
+ fields[:id] ||= get_random_uuid
21
21
  res = super(fields,params)
22
22
  user = User.load(fields[:user_id])
23
23
  user.clients << res.id
@@ -1,38 +1,36 @@
1
1
  require 'rest_client'
2
+ require 'rhoconnect/store'
2
3
 
3
4
  module Rhoconnect
4
5
  class Android
5
- class StaleAuthToken < Exception; end
6
+ C2DM_AUTHTOKEN_KEY = 'app:application:c2dm_auth_token'
6
7
  class InvalidAuthToken < Exception; end
7
8
  class AndroidPingError < Exception; end
8
9
 
9
10
  def self.ping(params)
11
+ already_retried = false
10
12
  begin
11
13
  settings = get_config(Rhoconnect.base_directory)[Rhoconnect.environment]
12
14
  authtoken = settings[:authtoken]
13
-
14
- RestClient.post(
15
- 'https://android.apis.google.com/c2dm/send', c2d_message(params),
16
- :authorization => "GoogleLogin auth=#{authtoken}"
17
- ) do |response, request, result, &block|
18
- # return exceptions based on response code & body
19
- case response.code
20
- when 200
21
- # TODO: Automate authtoken updates
22
- if response[:update_client_auth]
23
- raise StaleAuthToken.new(
24
- "Stale auth token, please update :authtoken: in settings.yml."
25
- )
26
- # body will contain the exception class
27
- elsif response.body =~ /^Error=(.*)$/
28
- raise AndroidPingError.new("Android ping error: #{$1 || ''}")
29
- else
30
- response.return!(request, result, &block)
31
- end
32
- when 401, 403
33
- raise InvalidAuthToken.new("Invalid auth token, please update :authtoken: in settings.yml.")
15
+
16
+ # obtain C2DM auth token
17
+ if not authtoken
18
+ authtoken = get_c2dm_token
19
+ if not already_retried
20
+ authtoken = "BadToken"
34
21
  end
35
22
  end
23
+
24
+ send_ping_to_device(authtoken, params)
25
+ rescue InvalidAuthToken => error
26
+ if not already_retried
27
+ Store.flash_data(C2DM_AUTHTOKEN_KEY)
28
+ already_retried = true
29
+ retry
30
+ end
31
+ log error
32
+ log error.backtrace.join("\n")
33
+ raise error
36
34
  rescue Exception => error
37
35
  log error
38
36
  log error.backtrace.join("\n")
@@ -40,6 +38,29 @@ module Rhoconnect
40
38
  end
41
39
  end
42
40
 
41
+ def self.send_ping_to_device(authtoken,params)
42
+ RestClient.post(
43
+ 'https://android.apis.google.com/c2dm/send', c2d_message(params),
44
+ :authorization => "GoogleLogin auth=#{authtoken}"
45
+ ) do |response, request, result, &block|
46
+ # return exceptions based on response code & body
47
+ case response.code
48
+ when 200
49
+ if response.body =~ /^Error=(.*)$/
50
+ raise AndroidPingError.new("Android ping error: #{$1 || ''}")
51
+ else
52
+ # store new token in redis for future calls
53
+ if response[:update_client_auth]
54
+ Store.put_value(C2DM_AUTHTOKEN_KEY, response[:update_client_auth])
55
+ end
56
+ response.return!(request, result, &block)
57
+ end
58
+ when 401, 403
59
+ raise InvalidAuthToken.new("Invalid or expired auth token. Obtain new authtoken from C2DM service.")
60
+ end
61
+ end
62
+ end
63
+
43
64
  def self.c2d_message(params)
44
65
  params.reject! {|k,v| v.nil? || v.length == 0}
45
66
  data = {}
@@ -52,5 +73,44 @@ module Rhoconnect
52
73
  data['data.phone_id'] = params['phone_id'] if params['phone_id']
53
74
  data
54
75
  end
76
+
77
+ def self.get_c2dm_token
78
+ if Store.exists? C2DM_AUTHTOKEN_KEY
79
+ return Store.get_value(C2DM_AUTHTOKEN_KEY)
80
+ end
81
+
82
+ settings = get_config(Rhoconnect.base_directory)[Rhoconnect.environment]
83
+ data = {}
84
+ data['accountType'] = 'HOSTED_OR_GOOGLE'
85
+ data['Email'] = settings[:c2dm_username]
86
+ data['Passwd'] = settings[:c2dm_passwd]
87
+ data['service'] = 'ac2dm'
88
+
89
+ urldata = ''
90
+ data.each do |key,value|
91
+ urldata += '&' + key.to_s + '=' + value.to_s
92
+ end
93
+ urldata[0,1] = ''
94
+
95
+ RestClient.post(
96
+ 'https://www.google.com/accounts/ClientLogin', urldata,
97
+ :content_type => "application/x-www-form-urlencoded"
98
+ ) do |response, request, result, &block|
99
+ # return exceptions based on response code & body
100
+ case response.code
101
+ when 200
102
+ authdata = /.*Auth=(.*)\n.*/.match(response.body)
103
+ Store.put_value(C2DM_AUTHTOKEN_KEY, authdata[1])
104
+ return authdata[1]
105
+ else
106
+ error_code = "Can not obtain auth token from C2DM service. Make sure that your C2DM credentials are valid."
107
+ if response.body =~ /^Error=(.*)$/
108
+ error_code += ': ' + response.body.to_s
109
+ end
110
+ raise InvalidAuthToken.new(error_code)
111
+ end
112
+ end
113
+ nil
114
+ end
55
115
  end
56
116
  end
@@ -1,3 +1,3 @@
1
1
  module Rhoconnect
2
- VERSION = '3.0.4'
2
+ VERSION = '3.0.5'
3
3
  end
data/spec/client_spec.rb CHANGED
@@ -103,6 +103,7 @@ describe "Client" do
103
103
  it "should increment clients stats on create" do
104
104
  Time.stub!(:now).and_return(10)
105
105
  Rhoconnect.stats = true
106
+ @c_fields.delete(:id)
106
107
  Client.create(@c_fields,{:source_name => @s_fields[:name]})
107
108
  Rhoconnect::Stats::Record.range('clients',0,-1).should == ["2:10"]
108
109
  Store.get_value('client:count').should == "2"
@@ -112,6 +113,7 @@ describe "Client" do
112
113
  it "should decrement clients stats on delete" do
113
114
  Time.stub!(:now).and_return(10)
114
115
  Rhoconnect.stats = true
116
+ @c_fields.delete(:id)
115
117
  c = Client.create(@c_fields,{:source_name => @s_fields[:name]})
116
118
  Rhoconnect::Stats::Record.range('clients',0,-1).should == ["2:10"]
117
119
  c.delete
@@ -274,6 +274,7 @@ describe "ClientSync" do
274
274
  describe "search" do
275
275
  before(:each) do
276
276
  @s_fields[:name] = 'SimpleAdapter'
277
+ @c_fields.delete(:id)
277
278
  @c1 = Client.create(@c_fields,{:source_name => @s_fields[:name]})
278
279
  @s1 = Source.create(@s_fields,@s_params)
279
280
  @cs1 = ClientSync.new(@s1,@c1,2)
@@ -89,8 +89,10 @@ describe "PingJob" do
89
89
  "sources" => [@s.name], "message" => 'hello world',
90
90
  "vibrate" => '5', "badge" => '5', "sound" => 'hello.mp3',"phone_id"=>nil}
91
91
  # another client with the same device pin ...
92
+ @c_fields.delete(:id)
92
93
  @c1 = Client.create(@c_fields,{:source_name => @s_fields[:name]})
93
94
  # and yet another one ...
95
+ @c_fields.delete(:id)
94
96
  @c2 = Client.create(@c_fields,{:source_name => @s_fields[:name]})
95
97
 
96
98
  Apple.should_receive(:ping).with({'device_pin' => @c.device_pin, 'device_port' => @c.device_port}.merge!(params))
@@ -105,8 +107,10 @@ describe "PingJob" do
105
107
  @c.phone_id = '3'
106
108
  @c_fields.merge!(:phone_id => '3')
107
109
  # another client with the same phone id..
110
+ @c_fields.delete(:id)
108
111
  @c1 = Client.create(@c_fields,{:source_name => @s_fields[:name]})
109
112
  # yet another...
113
+ @c_fields.delete(:id)
110
114
  @c2 = Client.create(@c_fields,{:source_name => @s_fields[:name]})
111
115
 
112
116
  Apple.should_receive(:ping).with({'device_pin' => @c.device_pin, 'phone_id' => @c.phone_id, 'device_port' => @c.device_port}.merge!(params))
@@ -19,6 +19,7 @@ describe "Ping Android" do
19
19
  end
20
20
 
21
21
  it "should ping android successfully" do
22
+ Store.put_value(Android::C2DM_AUTHTOKEN_KEY, 'ValidAuthToken')
22
23
  result = 'id=0:34234234134254%abc123\n'
23
24
  @response.stub!(:code).and_return(200)
24
25
  @response.stub!(:body).and_return(result)
@@ -27,7 +28,7 @@ describe "Ping Android" do
27
28
  setup_post_yield(@response)
28
29
  Android.ping(@params).body.should == result
29
30
  end
30
-
31
+
31
32
  it "should ping android with 503 connection error" do
32
33
  error = 'Connection refused'
33
34
  @response.stub!(:body).and_return(error)
@@ -37,6 +38,7 @@ describe "Ping Android" do
37
38
  end
38
39
 
39
40
  it "should ping android with 200 error message" do
41
+ Store.put_value(Android::C2DM_AUTHTOKEN_KEY, 'ValidAuthToken')
40
42
  error = 'Error=QuotaExceeded'
41
43
  @response.stub!(:code).and_return(200)
42
44
  @response.stub!(:body).and_return(error)
@@ -45,25 +47,61 @@ describe "Ping Android" do
45
47
  Android.should_receive(:log).twice
46
48
  lambda { Android.ping(@params) }.should raise_error(Android::AndroidPingError, "Android ping error: QuotaExceeded")
47
49
  end
50
+
51
+ it "should fail to obtain auth token from C2DM without creds" do
52
+ error = 'Error=BadAuthentication'
53
+ @response.stub!(:code).and_return(403)
54
+ @response.stub!(:body).and_return(error)
55
+ @response.stub!(:[]).and_return(nil)
56
+ setup_post_yield(@response)
57
+ Android.should_receive(:log).twice
58
+ lambda { Android.ping(@params) }.should raise_error(Android::InvalidAuthToken, "Can not obtain auth token from C2DM service. Make sure that your C2DM credentials are valid.: Error=BadAuthentication")
59
+ end
60
+
61
+ it "should pass to obtain auth token from C2DM" do
62
+ result = "SID=somesid\nAuth=myauthtoken\n"
63
+ @response.stub!(:code).and_return(200)
64
+ @response.stub!(:body).and_return(result)
65
+ @response.stub!(:[]).and_return(nil)
66
+ setup_post_yield(@response)
67
+
68
+ Android.get_c2dm_token
69
+ Store.get_value(Android::C2DM_AUTHTOKEN_KEY).should == 'myauthtoken'
70
+ end
71
+
72
+ it "should fail with invalid C2DM token and call retry" do
73
+ Store.put_value(Android::C2DM_AUTHTOKEN_KEY, 'BadToken')
74
+ error = 'Error=BadAuthentication'
75
+ @response.stub!(:code).and_return(403)
76
+ @response.stub!(:body).and_return(error)
77
+ @response.stub!(:[]).and_return(nil)
78
+ setup_post_yield(@response)
79
+
80
+ Store.should_receive(:flash_data).once
81
+ Android.should_receive(:get_c2dm_token).twice.and_return('BadToken')
82
+ Android.should_receive(:log).twice
83
+ lambda { Android.ping(@params) }.should raise_error(Android::InvalidAuthToken, "Invalid or expired auth token. Obtain new authtoken from C2DM service.")
84
+ end
48
85
 
49
- it "should ping android with stale auth token" do
86
+ it "should ping android and receive Update-Client-Auth header" do
87
+ Store.put_value(Android::C2DM_AUTHTOKEN_KEY, 'ValidAuthToken')
50
88
  @response.stub!(:code).and_return(200)
51
89
  @response.stub!(:body).and_return('')
52
- @response.stub!(:[]).and_return({:update_client_auth => 'abc123'})
90
+ @response.stub!(:[]).and_return(:update_client_auth => 'abc123')
91
+ @response.stub!(:return!).and_return(@response)
53
92
  setup_post_yield(@response)
54
- Android.should_receive(:log).twice
55
- lambda { Android.ping(@params) }.should raise_error(
56
- Android::StaleAuthToken, "Stale auth token, please update :authtoken: in settings.yml."
57
- )
93
+ Android.ping(@params)
94
+ Store.get_value(Android::C2DM_AUTHTOKEN_KEY).should_not == 'ValidAuthToken'
58
95
  end
59
96
 
60
97
  it "should ping android with 401 error message" do
61
98
  @response.stub!(:code).and_return(401)
62
99
  @response.stub!(:body).and_return('')
63
100
  setup_post_yield(@response)
101
+ Android.should_receive(:get_c2dm_token).twice.and_return("ValidAuthToken")
64
102
  Android.should_receive(:log).twice
65
103
  lambda { Android.ping(@params) }.should raise_error(
66
- Android::InvalidAuthToken, "Invalid auth token, please update :authtoken: in settings.yml."
104
+ Android::InvalidAuthToken, "Invalid or expired auth token. Obtain new authtoken from C2DM service."
67
105
  )
68
106
  end
69
107