rhoconnect 3.0.4 → 3.0.5
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/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
|
|