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/CHANGELOG.md +5 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +7 -1
- data/Rakefile +1 -0
- data/doc/blob-sync.txt +9 -2
- data/doc/client-objc.txt +108 -42
- data/doc/deploying.txt +60 -94
- data/doc/install.txt +1 -1
- data/doc/push.txt +9 -1
- data/doc/rails-plugin.txt +8 -10
- data/doc/settings.txt +21 -0
- data/installer/unix-like/rho_connect_install_constants.rb +1 -1
- data/installer/utils/package_upload/repos.rake +5 -0
- data/installer/utils/package_upload/repos.rb +83 -0
- data/installer/utils/package_upload/s3_upload.rb +104 -0
- data/installer/windows/rhosync.nsi +5 -5
- data/lib/rhoconnect/api/application/clientregister.rb +9 -2
- data/lib/rhoconnect/client.rb +1 -1
- data/lib/rhoconnect/ping/android.rb +82 -22
- data/lib/rhoconnect/version.rb +1 -1
- data/spec/client_spec.rb +2 -0
- data/spec/client_sync_spec.rb +1 -0
- data/spec/jobs/ping_job_spec.rb +4 -0
- data/spec/ping/android_spec.rb +46 -8
- data/spec/server/server_spec.rb +13 -0
- data/spec/user_spec.rb +1 -0
- data/tasks/redis.rake +2 -2
- metadata +8 -5
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
|
-
|
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 [
|
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 [
|
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(
|
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
|
-
|
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",
|
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
|
-
|
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 (
|
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.
|
@@ -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.
|
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.
|
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.
|
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.
|
187
|
+
Push "$INSTDIR\redis-2.4.1"
|
188
188
|
Call EnvVarUpdate
|
189
189
|
Pop $R0
|
190
190
|
|
191
|
-
ExecWait '$INSTDIR\redis-2.
|
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
|
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
|
data/lib/rhoconnect/client.rb
CHANGED
@@ -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]
|
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
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
data/lib/rhoconnect/version.rb
CHANGED
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
|
data/spec/client_sync_spec.rb
CHANGED
@@ -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)
|
data/spec/jobs/ping_job_spec.rb
CHANGED
@@ -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))
|
data/spec/ping/android_spec.rb
CHANGED
@@ -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
|
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(
|
90
|
+
@response.stub!(:[]).and_return(:update_client_auth => 'abc123')
|
91
|
+
@response.stub!(:return!).and_return(@response)
|
53
92
|
setup_post_yield(@response)
|
54
|
-
|
55
|
-
|
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
|
104
|
+
Android::InvalidAuthToken, "Invalid or expired auth token. Obtain new authtoken from C2DM service."
|
67
105
|
)
|
68
106
|
end
|
69
107
|
|