rhosync 2.1.0.beta.1 → 2.1.0.beta.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +8 -0
- data/Rakefile +8 -3
- data/bench/benchapp/application.rb +1 -1
- data/examples/simple/application.rb +1 -1
- data/generators/templates/application/application.rb +1 -1
- data/generators/templates/source/source_adapter.rb +1 -1
- data/lib/rhosync/client.rb +3 -0
- data/lib/rhosync/client_sync.rb +93 -28
- data/lib/rhosync/jobs/bulk_data_job.rb +6 -6
- data/lib/rhosync/jobs/ping_job.rb +1 -1
- data/lib/rhosync/ping/android.rb +1 -0
- data/lib/rhosync/server.rb +4 -1
- data/lib/rhosync/source.rb +9 -1
- data/lib/rhosync/source_adapter.rb +7 -4
- data/lib/rhosync/source_sync.rb +43 -11
- data/lib/rhosync/version.rb +2 -2
- data/lib/rhosync.rb +15 -1
- data/spec/api/get_source_params_spec.rb +1 -1
- data/spec/api/rhosync_api_spec.rb +1 -1
- data/spec/apps/rhotestapp/settings/settings.yml +0 -12
- data/spec/apps/rhotestapp/sources/fixed_schema_adapter.rb +19 -0
- data/spec/client_spec.rb +10 -0
- data/spec/client_sync_spec.rb +120 -1
- data/spec/jobs/bulk_data_job_spec.rb +19 -0
- data/spec/jobs/ping_job_spec.rb +10 -0
- data/spec/ping/android_spec.rb +10 -0
- data/spec/rhosync_spec.rb +12 -0
- data/spec/server/server_spec.rb +13 -17
- data/spec/source_sync_spec.rb +24 -3
- data/spec/spec_helper.rb +22 -3
- metadata +13 -13
data/CHANGELOG
CHANGED
@@ -6,6 +6,14 @@
|
|
6
6
|
* #5821277 - http stats by source not showing
|
7
7
|
* #5899454 - move lock prefix to beginning so we don't return stats keys with it
|
8
8
|
* #5822966 - bulk sync data file cannot handle space in the username
|
9
|
+
* #6450519 - blob sync resend_page doesn't send metadata
|
10
|
+
* #4646791 - cryptic error message if client exists, but source name is bogus
|
11
|
+
* #6827511 - fill in schema column in bulk sync file sources table
|
12
|
+
* #4490679 - support schema method in source adapter (runtime schema for bulk data)
|
13
|
+
* #6573429 - if schema changed in any adapter, invalidate bulk data file
|
14
|
+
* #7034095 - don't ping device if device_pin is empty or nil
|
15
|
+
* #7089047 - fixed application.rb template store_blob method
|
16
|
+
* #7055889 - fixed schema tables should have 'object' primary key
|
9
17
|
|
10
18
|
## 2.0.9
|
11
19
|
* #5154725 - stats framework
|
data/Rakefile
CHANGED
@@ -18,7 +18,6 @@ begin
|
|
18
18
|
:jobs => 'spec/jobs/*_spec.rb',
|
19
19
|
:stats => 'spec/stats/*_spec.rb',
|
20
20
|
:ping => 'spec/ping/*_spec.rb',
|
21
|
-
:doc => 'spec/doc/*_spec.rb',
|
22
21
|
:generator => 'spec/generator/*_spec.rb',
|
23
22
|
:bench => 'bench/spec/*_spec.rb'
|
24
23
|
}
|
@@ -37,6 +36,12 @@ begin
|
|
37
36
|
t.rcov_opts = ['--exclude', 'spec/*,gems/*,apps/*,bench/spec/*,json/*']
|
38
37
|
end
|
39
38
|
|
39
|
+
desc "Run doc generator - dumps out doc/protocol.html"
|
40
|
+
Spec::Rake::SpecTask.new('doc') do |t|
|
41
|
+
t.spec_files = FileList['spec/doc/*_spec.rb']
|
42
|
+
t.rcov = false
|
43
|
+
end
|
44
|
+
|
40
45
|
rescue LoadError => e
|
41
46
|
puts "rspec / rcov not available. Install with: "
|
42
47
|
puts "gem install rspec rcov\n\n"
|
@@ -55,7 +60,7 @@ begin
|
|
55
60
|
gemspec.homepage = %q{http://rhomobile.com/products/rhosync}
|
56
61
|
gemspec.authors = ["Rhomobile"]
|
57
62
|
gemspec.email = %q{dev@rhomobile.com}
|
58
|
-
gemspec.version =
|
63
|
+
gemspec.version = '2.1.0.beta.2'
|
59
64
|
gemspec.files = FileList["[A-Z]*", "{bench,bin,generators,lib,spec,tasks}/**/*"]
|
60
65
|
|
61
66
|
# TODO: Due to https://www.pivotaltracker.com/story/show/3417862, we can't use JSON 1.4.3
|
@@ -67,7 +72,7 @@ begin
|
|
67
72
|
gemspec.add_dependency "redis", "~>2.0.0"
|
68
73
|
gemspec.add_dependency "resque", ">=1.9.7"
|
69
74
|
gemspec.add_dependency "rest-client", ">=1.4.2"
|
70
|
-
gemspec.add_dependency "sinatra", "~>1.
|
75
|
+
gemspec.add_dependency "sinatra", "~>1.1"
|
71
76
|
gemspec.add_dependency "templater", "~>1.0.0"
|
72
77
|
gemspec.add_dependency "rake", ">=0.8.7"
|
73
78
|
gemspec.add_development_dependency "jeweler", ">=1.4.0"
|
@@ -18,7 +18,7 @@ class Application < Rhosync::Base
|
|
18
18
|
# Override this by creating a copy of the file somewhere
|
19
19
|
# and returning the path to that file (then don't call super!):
|
20
20
|
# i.e. /mnt/myimages/soccer.png
|
21
|
-
def store_blob(blob)
|
21
|
+
def store_blob(obj,field_name,blob)
|
22
22
|
super #=> returns blob[:tempfile]
|
23
23
|
end
|
24
24
|
end
|
@@ -18,7 +18,7 @@ class Application < Rhosync::Base
|
|
18
18
|
# Override this by creating a copy of the file somewhere
|
19
19
|
# and returning the path to that file (then don't call super!):
|
20
20
|
# i.e. /mnt/myimages/soccer.png
|
21
|
-
def store_blob(blob)
|
21
|
+
def store_blob(obj,field_name,blob)
|
22
22
|
super #=> returns blob[:tempfile]
|
23
23
|
end
|
24
24
|
end
|
@@ -18,7 +18,7 @@ class Application < Rhosync::Base
|
|
18
18
|
# Override this by creating a copy of the file somewhere
|
19
19
|
# and returning the path to that file (then don't call super!):
|
20
20
|
# i.e. /mnt/myimages/soccer.png
|
21
|
-
def store_blob(blob)
|
21
|
+
def store_blob(object,field_name,blob)
|
22
22
|
super #=> returns blob[:tempfile]
|
23
23
|
end
|
24
24
|
end
|
@@ -40,7 +40,7 @@ class <%=class_name%> < SourceAdapter
|
|
40
40
|
# TODO: write some code here if applicable
|
41
41
|
# be sure to have a hash key and value for "object"
|
42
42
|
# for now, we'll say that its OK to not have a delete operation
|
43
|
-
# raise "Please provide some code to delete a single object in the backend application using the
|
43
|
+
# raise "Please provide some code to delete a single object in the backend application using the object_id"
|
44
44
|
end
|
45
45
|
|
46
46
|
def logoff
|
data/lib/rhosync/client.rb
CHANGED
@@ -60,12 +60,15 @@ module Rhosync
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def update_clientdoc(sources)
|
63
|
+
# TODO: We need to store schema info and data info in bulk data
|
64
|
+
# source masterdoc and source schema might have changed!
|
63
65
|
sources.each do |source|
|
64
66
|
s = Source.load(source,{:app_id => app_id,:user_id => user_id})
|
65
67
|
unless s.sync_type.to_sym == :bulk_sync_only
|
66
68
|
self.source_name = source
|
67
69
|
Store.clone(s.docname(:md_copy),self.docname(:cd))
|
68
70
|
end
|
71
|
+
self.put_value(:schema_sha1,s.get_value(:schema_sha1))
|
69
72
|
end
|
70
73
|
end
|
71
74
|
|
data/lib/rhosync/client_sync.rb
CHANGED
@@ -12,22 +12,30 @@ module Rhosync
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def receive_cud(cud_params={},query_params=nil)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
processed
|
15
|
+
if @source.is_pass_through
|
16
|
+
@source_sync.pass_through_cud(cud_params,query_params) if value
|
17
|
+
else
|
18
|
+
_process_blobs(cud_params)
|
19
|
+
processed = 0
|
20
|
+
['create','update','delete'].each do |op|
|
21
|
+
key,value = op,cud_params[op]
|
22
|
+
processed += _receive_cud(key,value) if value
|
23
|
+
end
|
24
|
+
@source_sync.process_cud(@client.id) if processed > 0
|
20
25
|
end
|
21
|
-
@source_sync.process_cud(@client.id) if processed > 0
|
22
26
|
end
|
23
27
|
|
24
28
|
def send_cud(token=nil,query_params=nil)
|
25
29
|
res = []
|
26
|
-
if not _ack_token(token)
|
30
|
+
if not _ack_token(token) and not @source.is_pass_through?
|
27
31
|
res = resend_page(token)
|
28
32
|
else
|
29
|
-
@source_sync.process_query(query_params)
|
30
|
-
|
33
|
+
query_result = @source_sync.process_query(query_params)
|
34
|
+
if @source.is_pass_through?
|
35
|
+
res = send_pass_through_data(query_result)
|
36
|
+
else
|
37
|
+
res = send_new_page
|
38
|
+
end
|
31
39
|
end
|
32
40
|
_format_result(res[0],res[1],res[2],res[3])
|
33
41
|
end
|
@@ -53,39 +61,85 @@ module Rhosync
|
|
53
61
|
end
|
54
62
|
|
55
63
|
def send_new_page
|
64
|
+
token,progress_count,total_count,res = '',0,0,{}
|
65
|
+
if schema_changed?
|
66
|
+
_expire_bulk_data
|
67
|
+
token = compute_token(@client.docname(:page_token))
|
68
|
+
res = {'schema-changed' => 'true'}
|
69
|
+
else
|
70
|
+
compute_errors_page
|
71
|
+
res = build_page do |r|
|
72
|
+
progress_count,total_count,r['insert'] = compute_page
|
73
|
+
r['delete'] = compute_deleted_page
|
74
|
+
r['links'] = compute_links_page
|
75
|
+
r['metadata'] = compute_metadata
|
76
|
+
end
|
77
|
+
if res['insert'] or res['delete'] or res['links']
|
78
|
+
token = compute_token(@client.docname(:page_token))
|
79
|
+
else
|
80
|
+
_delete_errors_page
|
81
|
+
end
|
82
|
+
@client.put_data(:cd,res['insert'],true)
|
83
|
+
@client.delete_data(:cd,res['delete'])
|
84
|
+
end
|
85
|
+
[token,progress_count,total_count,res]
|
86
|
+
end
|
87
|
+
|
88
|
+
def send_pass_through_data(data)
|
89
|
+
data ||= {}
|
90
|
+
data.each_key do |object_id|
|
91
|
+
data[object_id].each { |attrib,value| data[object_id][attrib] = '' if value.nil? }
|
92
|
+
end
|
93
|
+
token = ''
|
56
94
|
compute_errors_page
|
57
|
-
token,progress_count,total_count = '',0,0
|
58
95
|
res = build_page do |r|
|
59
|
-
|
60
|
-
r['delete'] = compute_deleted_page
|
61
|
-
r['links'] = compute_links_page
|
96
|
+
r['insert'] = data
|
62
97
|
r['metadata'] = compute_metadata
|
63
98
|
end
|
64
|
-
if res['insert']
|
99
|
+
if res['insert']
|
65
100
|
token = compute_token(@client.docname(:page_token))
|
66
101
|
else
|
67
102
|
_delete_errors_page
|
68
103
|
end
|
69
|
-
|
70
|
-
@client.delete_data(:cd,res['delete'])
|
71
|
-
[token,progress_count,total_count,res]
|
104
|
+
[token,0,data.size,res]
|
72
105
|
end
|
73
106
|
|
74
107
|
# Resend token for a client, also sends exceptions
|
75
108
|
def resend_page(token=nil)
|
76
|
-
token,progress_count,total_count = '',0,0
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
109
|
+
token,progress_count,total_count,res = '',0,0,{}
|
110
|
+
schema_page = @client.get_value(:schema_page)
|
111
|
+
if schema_page
|
112
|
+
res = {'schema-changed' => 'true'}
|
113
|
+
else
|
114
|
+
res = build_page do |r|
|
115
|
+
r['insert'] = @client.get_data(:page)
|
116
|
+
r['delete'] = @client.get_data(:delete_page)
|
117
|
+
r['links'] = @client.get_data(:create_links_page)
|
118
|
+
r['metadata'] = @client.get_value(:metadata_page)
|
119
|
+
progress_count = @client.get_value(:cd_size).to_i
|
120
|
+
total_count = @client.get_value(:total_count_page).to_i
|
121
|
+
end
|
84
122
|
end
|
85
123
|
token = @client.get_value(:page_token)
|
86
124
|
[token,progress_count,total_count,res]
|
87
125
|
end
|
88
126
|
|
127
|
+
# Checks if schema changed
|
128
|
+
def schema_changed?
|
129
|
+
schema_sha1 = @source.get_value(:schema_sha1)
|
130
|
+
|
131
|
+
if @client.get_value(:schema_sha1).nil?
|
132
|
+
@client.put_value(:schema_sha1,schema_sha1)
|
133
|
+
return false
|
134
|
+
elsif @client.get_value(:schema_sha1) == schema_sha1
|
135
|
+
return false
|
136
|
+
end
|
137
|
+
|
138
|
+
@client.put_value(:schema_sha1,schema_sha1)
|
139
|
+
@client.put_value(:schema_page,schema_sha1)
|
140
|
+
true
|
141
|
+
end
|
142
|
+
|
89
143
|
# Computes the metadata sha1 and returns metadata if client's sha1 doesn't
|
90
144
|
# match source's sha1
|
91
145
|
def compute_metadata
|
@@ -94,9 +148,11 @@ module Rhosync
|
|
94
148
|
end
|
95
149
|
return if @client.get_value(:metadata_sha1) == metadata_sha1
|
96
150
|
@client.put_value(:metadata_sha1,metadata_sha1)
|
151
|
+
@client.put_value(:metadata_page,metadata)
|
97
152
|
metadata
|
98
153
|
end
|
99
154
|
|
155
|
+
|
100
156
|
# Computes diffs between master doc and client doc, trims it to page size,
|
101
157
|
# stores page, and returns page as hash
|
102
158
|
def compute_page
|
@@ -215,6 +271,14 @@ module Rhosync
|
|
215
271
|
end
|
216
272
|
|
217
273
|
private
|
274
|
+
|
275
|
+
# expires the bulk data for the client
|
276
|
+
def _expire_bulk_data
|
277
|
+
[:user,:app].each do |partition|
|
278
|
+
Rhosync.expire_bulk_data(@client.user_id,partition)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
218
282
|
def _resend_search_result
|
219
283
|
res = @client.get_data(:search_page)
|
220
284
|
_format_search_result(res,res.size)
|
@@ -234,8 +298,8 @@ module Rhosync
|
|
234
298
|
def _do_search(params={})
|
235
299
|
# call source adapter search unless client is sending token for ack
|
236
300
|
search_params = params[:search] if params
|
237
|
-
@source_sync.search(@client.id,search_params) if params.nil? or !params[:token]
|
238
|
-
res,diffsize = compute_search
|
301
|
+
res = @source_sync.search(@client.id,search_params) if params.nil? or !params[:token]
|
302
|
+
res,diffsize = @source.is_pass_through? ? [res,res.size] : compute_search
|
239
303
|
formatted_res = _format_search_result(res,diffsize)
|
240
304
|
@client.flash_data('search*') if diffsize == 0
|
241
305
|
formatted_res
|
@@ -250,7 +314,6 @@ module Rhosync
|
|
250
314
|
else
|
251
315
|
search_token = @client.get_value(:search_token)
|
252
316
|
search_token ||= ''
|
253
|
-
#res,diffsize = compute_search
|
254
317
|
return [] if res.empty?
|
255
318
|
[ {'version'=>VERSION},
|
256
319
|
{'token' => search_token},
|
@@ -285,6 +348,8 @@ module Rhosync
|
|
285
348
|
if stored_token
|
286
349
|
if token and stored_token == token
|
287
350
|
@client.put_value(:page_token,nil)
|
351
|
+
@client.flash_data(:schema_page)
|
352
|
+
@client.flash_data(:metadata_page)
|
288
353
|
@client.flash_data(:create_links_page)
|
289
354
|
@client.flash_data(:page)
|
290
355
|
@client.flash_data(:delete_page)
|
@@ -55,7 +55,7 @@ module Rhosync
|
|
55
55
|
data = source.get_data(:md)
|
56
56
|
counter = {}
|
57
57
|
columns,qm = [],[]
|
58
|
-
create_table = ["\"object\" varchar"]
|
58
|
+
create_table = ["\"object\" varchar(255) PRIMARY KEY"]
|
59
59
|
schema = JSON.parse(source.schema)
|
60
60
|
|
61
61
|
db.transaction do |database|
|
@@ -84,7 +84,7 @@ module Rhosync
|
|
84
84
|
schema['index'].each do |key,value|
|
85
85
|
val2 = ""
|
86
86
|
value.split(',').each do |col|
|
87
|
-
val2 += ',' if val2.length
|
87
|
+
val2 += ',' if val2.length > 0
|
88
88
|
val2 += "\"#{col}\""
|
89
89
|
end
|
90
90
|
|
@@ -95,7 +95,7 @@ module Rhosync
|
|
95
95
|
schema['unique_index'].each do |key,value|
|
96
96
|
val2 = ""
|
97
97
|
value.split(',').each do |col|
|
98
|
-
val2 += ',' if val2.length
|
98
|
+
val2 += ',' if val2.length > 0
|
99
99
|
val2 += "\"#{col}\""
|
100
100
|
end
|
101
101
|
|
@@ -117,12 +117,12 @@ module Rhosync
|
|
117
117
|
def self.populate_sources_table(db,sources_refs)
|
118
118
|
db.transaction do |database|
|
119
119
|
database.prepare("insert into sources
|
120
|
-
(source_id,name,sync_priority,partition,sync_type,source_attribs,metadata,blob_attribs,associations)
|
121
|
-
values (
|
120
|
+
(source_id,name,sync_priority,partition,sync_type,source_attribs,metadata,schema,blob_attribs,associations)
|
121
|
+
values (?,?,?,?,?,?,?,?,?,?)") do |stmt|
|
122
122
|
sources_refs.each do |source_name,ref|
|
123
123
|
s = ref[:source]
|
124
124
|
stmt.execute(s.source_id,s.name,s.priority,s.partition_type,
|
125
|
-
s.sync_type,refs_to_s(ref[:refs]),s.get_value(:metadata),s.blob_attribs,s.has_many)
|
125
|
+
s.sync_type,refs_to_s(ref[:refs]),s.get_value(:metadata),s.schema,s.blob_attribs,s.has_many)
|
126
126
|
end
|
127
127
|
end
|
128
128
|
end
|
@@ -11,7 +11,7 @@ module Rhosync
|
|
11
11
|
client = Client.load(client_id,{:source_name => '*'})
|
12
12
|
params.merge!('device_port' => client.device_port,
|
13
13
|
'device_pin' => client.device_pin)
|
14
|
-
if client.device_type and client.device_type.size > 0
|
14
|
+
if client.device_type and client.device_type.size > 0 and client.device_pin and client.device_pin.size > 0
|
15
15
|
klass = Object.const_get(camelize(client.device_type.downcase))
|
16
16
|
klass.ping(params) if klass
|
17
17
|
else
|
data/lib/rhosync/ping/android.rb
CHANGED
data/lib/rhosync/server.rb
CHANGED
@@ -100,8 +100,11 @@ module Rhosync
|
|
100
100
|
user = current_user
|
101
101
|
if params[:source_name] and user
|
102
102
|
@source = Source.load(params[:source_name],
|
103
|
-
{:user_id => user.login,:app_id => APP_NAME})
|
103
|
+
{:user_id => user.login,:app_id => APP_NAME})
|
104
|
+
raise "ERROR: Source '#{params[:source_name]}' requested by client doesn't exist.\n" unless @source
|
105
|
+
@source
|
104
106
|
else
|
107
|
+
log "ERROR: Can't load source, no source_name provided.\n"
|
105
108
|
nil
|
106
109
|
end
|
107
110
|
end
|
data/lib/rhosync/source.rb
CHANGED
@@ -15,7 +15,7 @@ module Rhosync
|
|
15
15
|
field :queue,:string
|
16
16
|
field :query_queue,:string
|
17
17
|
field :cud_queue,:string
|
18
|
-
field :
|
18
|
+
field :pass_through,:string
|
19
19
|
attr_accessor :app_id, :user_id
|
20
20
|
validates_presence_of :name #, :source_id
|
21
21
|
|
@@ -100,6 +100,10 @@ module Rhosync
|
|
100
100
|
@app ||= App.load(self.app_id)
|
101
101
|
end
|
102
102
|
|
103
|
+
def schema
|
104
|
+
@schema ||= self.get_value(:schema)
|
105
|
+
end
|
106
|
+
|
103
107
|
def read_state
|
104
108
|
id = {:app_id => self.app_id,:user_id => user_by_partition,
|
105
109
|
:source_name => self.name}
|
@@ -142,6 +146,10 @@ module Rhosync
|
|
142
146
|
yield client_id,params if need_refresh
|
143
147
|
end
|
144
148
|
|
149
|
+
def is_pass_through?
|
150
|
+
self.pass_through and self.pass_through == 'true'
|
151
|
+
end
|
152
|
+
|
145
153
|
private
|
146
154
|
def self.validate_attributes(params)
|
147
155
|
raise ArgumentError.new('Missing required attribute user_id') unless params[:user_id]
|
@@ -62,7 +62,12 @@ module Rhosync
|
|
62
62
|
@tmp_docname = @source.docname(:md) + get_random_uuid
|
63
63
|
@stash_size = 0
|
64
64
|
params ? self.query(params) : self.query
|
65
|
-
|
65
|
+
if @source.is_pass_through?
|
66
|
+
@result
|
67
|
+
else
|
68
|
+
self.sync
|
69
|
+
true
|
70
|
+
end
|
66
71
|
end
|
67
72
|
|
68
73
|
def stash_result
|
@@ -73,9 +78,7 @@ module Rhosync
|
|
73
78
|
end
|
74
79
|
|
75
80
|
def expire_bulk_data(partition = :user)
|
76
|
-
|
77
|
-
data = BulkData.load(name)
|
78
|
-
data.refresh_time = Time.now.to_i if data
|
81
|
+
Rhosync.expire_bulk_data(current_user.login,partition)
|
79
82
|
end
|
80
83
|
|
81
84
|
def create(name_value_list); end
|
data/lib/rhosync/source_sync.rb
CHANGED
@@ -22,6 +22,33 @@ module Rhosync
|
|
22
22
|
_measure_and_process_cud('delete',client_id)
|
23
23
|
end
|
24
24
|
|
25
|
+
# Pass through CUD to adapter, no data stored
|
26
|
+
def pass_through_cud(cud_params,query_params)
|
27
|
+
res,processed_objects = {},[]
|
28
|
+
begin
|
29
|
+
['create','update','delete'].each do |op|
|
30
|
+
key,objects = op,cud_params[op]
|
31
|
+
objects.each do |key,value|
|
32
|
+
case op
|
33
|
+
when 'create'
|
34
|
+
@adapter.send(op.to_sym,value)
|
35
|
+
when 'update'
|
36
|
+
value['id'] = key
|
37
|
+
@adapter.send(op.to_sym,value)
|
38
|
+
when 'delete'
|
39
|
+
@adapter.send(op.to_sym,key)
|
40
|
+
end
|
41
|
+
process_objects << key
|
42
|
+
end if objects
|
43
|
+
end
|
44
|
+
rescue Exception => e
|
45
|
+
log "Error in #{op} pass through method: #{e.message}"
|
46
|
+
res['error'] = { 'operation' => op, 'message' => e.message }
|
47
|
+
end
|
48
|
+
res['processed'] = process_objects
|
49
|
+
res.to_json
|
50
|
+
end
|
51
|
+
|
25
52
|
# Read Operation; params are query arguments
|
26
53
|
def read(client_id=nil,params=nil)
|
27
54
|
_read('query',client_id,params)
|
@@ -59,13 +86,15 @@ module Rhosync
|
|
59
86
|
end
|
60
87
|
|
61
88
|
def do_query(params=nil)
|
89
|
+
result = nil
|
62
90
|
@source.if_need_refresh do
|
63
91
|
Stats::Record.update("source:query:#{@source.name}") do
|
64
92
|
return if _auth_op('login') == false
|
65
|
-
self.read(nil,params)
|
93
|
+
result = self.read(nil,params)
|
66
94
|
_auth_op('logoff')
|
67
95
|
end
|
68
96
|
end
|
97
|
+
result
|
69
98
|
end
|
70
99
|
|
71
100
|
# Enqueue a job for the source based on job type
|
@@ -210,12 +239,12 @@ module Rhosync
|
|
210
239
|
end
|
211
240
|
|
212
241
|
# Metadata Operation; source adapter returns json
|
213
|
-
def
|
214
|
-
if @adapter.respond_to?(
|
215
|
-
|
216
|
-
if
|
217
|
-
@source.put_value(
|
218
|
-
@source.put_value(
|
242
|
+
def _get_data(method)
|
243
|
+
if @adapter.respond_to?(method)
|
244
|
+
data = @adapter.send(method)
|
245
|
+
if data
|
246
|
+
@source.put_value(method,data)
|
247
|
+
@source.put_value("#{method}_sha1",Digest::SHA1.hexdigest(data))
|
219
248
|
end
|
220
249
|
end
|
221
250
|
end
|
@@ -223,17 +252,20 @@ module Rhosync
|
|
223
252
|
# Read Operation; params are query arguments
|
224
253
|
def _read(operation,client_id,params=nil)
|
225
254
|
errordoc = nil
|
255
|
+
result = nil
|
226
256
|
begin
|
227
257
|
if operation == 'search'
|
228
258
|
client = Client.load(client_id,{:source_name => @source.name})
|
229
259
|
errordoc = client.docname(:search_errors)
|
230
260
|
compute_token(client.docname(:search_token))
|
231
|
-
@adapter.search(params)
|
261
|
+
result = @adapter.search(params)
|
232
262
|
@adapter.save(client.docname(:search))
|
233
263
|
else
|
234
264
|
errordoc = @source.docname(:errors)
|
235
|
-
|
236
|
-
|
265
|
+
[:metadata,:schema].each do |method|
|
266
|
+
_get_data(method)
|
267
|
+
end
|
268
|
+
result = @adapter.do_query(params)
|
237
269
|
end
|
238
270
|
# operation,sync succeeded, remove errors
|
239
271
|
Store.lock(errordoc) do
|
@@ -246,7 +278,7 @@ module Rhosync
|
|
246
278
|
Store.put_data(errordoc,{"#{operation}-error"=>{'message'=>e.message}},true)
|
247
279
|
end
|
248
280
|
end
|
249
|
-
|
281
|
+
result
|
250
282
|
end
|
251
283
|
end
|
252
284
|
end
|
data/lib/rhosync/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Rhosync
|
2
|
-
VERSION = '2.1.0
|
3
|
-
end
|
2
|
+
VERSION = '2.1.0'
|
3
|
+
end
|
data/lib/rhosync.rb
CHANGED
@@ -91,7 +91,8 @@ module Rhosync
|
|
91
91
|
end
|
92
92
|
sources = config[:sources] || []
|
93
93
|
sources.each do |source_name,fields|
|
94
|
-
|
94
|
+
check_for_schema_field!(fields)
|
95
|
+
if Source.is_exist?(source_name)
|
95
96
|
s = Source.load(source_name,{:app_id => app.name,:user_id => '*'})
|
96
97
|
s.update(fields)
|
97
98
|
else
|
@@ -143,6 +144,13 @@ module Rhosync
|
|
143
144
|
log "*"*60
|
144
145
|
end
|
145
146
|
end
|
147
|
+
|
148
|
+
def check_for_schema_field!(fields)
|
149
|
+
if fields['schema']
|
150
|
+
log "ERROR: 'schema' field in settings.yml is not supported anymore, please use source adapter schema method!"
|
151
|
+
exit(1)
|
152
|
+
end
|
153
|
+
end
|
146
154
|
|
147
155
|
# Serializes oav to set element
|
148
156
|
def setelement(obj,attrib,value)
|
@@ -200,6 +208,12 @@ module Rhosync
|
|
200
208
|
log "*"*60
|
201
209
|
end
|
202
210
|
end
|
211
|
+
|
212
|
+
def expire_bulk_data(username, partition = :user)
|
213
|
+
name = BulkData.get_name(partition,username)
|
214
|
+
data = BulkData.load(name)
|
215
|
+
data.refresh_time = Time.now.to_i if data
|
216
|
+
end
|
203
217
|
|
204
218
|
def unzip_file(file_dir,params)
|
205
219
|
uploaded_file = File.join(file_dir, params[:filename])
|
@@ -22,7 +22,7 @@ describe "RhosyncApiGetSourceParams" do
|
|
22
22
|
{"name"=>"queue", "value"=>nil, "type"=>"string"},
|
23
23
|
{"name"=>"query_queue", "value"=>nil, "type"=>"string"},
|
24
24
|
{"name"=>"cud_queue", "value"=>nil, "type"=>"string"},
|
25
|
-
{"name"=>"
|
25
|
+
{"name"=>"pass_through", "value"=>nil, "type"=>"string"}]
|
26
26
|
end
|
27
27
|
|
28
28
|
end
|
@@ -169,7 +169,7 @@ describe "RhosyncApi" do
|
|
169
169
|
{"name"=>"queue", "value"=>nil, "type"=>"string"},
|
170
170
|
{"name"=>"query_queue", "value"=>nil, "type"=>"string"},
|
171
171
|
{"name"=>"cud_queue", "value"=>nil, "type"=>"string"},
|
172
|
-
{"name"=>"
|
172
|
+
{"name"=>"pass_through", "value"=>nil, "type"=>"string"}]
|
173
173
|
end
|
174
174
|
|
175
175
|
it "should list source attributes using rest call" do
|
@@ -9,18 +9,6 @@
|
|
9
9
|
sync_type: 'incremental'
|
10
10
|
belongs_to:
|
11
11
|
- brand: 'SampleAdapter'
|
12
|
-
schema:
|
13
|
-
version: '1.0'
|
14
|
-
property:
|
15
|
-
name: 'string'
|
16
|
-
brand: 'string'
|
17
|
-
price: 'string'
|
18
|
-
image_url_cropped: 'blob,overwrite'
|
19
|
-
image_url: 'blob'
|
20
|
-
index:
|
21
|
-
by_name_brand: 'name,brand'
|
22
|
-
unique_index:
|
23
|
-
by_price: 'price'
|
24
12
|
|
25
13
|
:development:
|
26
14
|
:licensefile: settings/license.key
|
@@ -6,4 +6,23 @@ class FixedSchemaAdapter < SourceAdapter
|
|
6
6
|
def query(params=nil)
|
7
7
|
@result = Store.get_data('test_db_storage')
|
8
8
|
end
|
9
|
+
|
10
|
+
def schema
|
11
|
+
{
|
12
|
+
'version' => '1.0',
|
13
|
+
'property' => {
|
14
|
+
'name' => 'string',
|
15
|
+
'brand' => 'string',
|
16
|
+
'price' => 'string',
|
17
|
+
'image_url_cropped' => 'blob,overwrite',
|
18
|
+
'image_url' => 'blob'
|
19
|
+
},
|
20
|
+
'index' => {
|
21
|
+
'by_name_brand' => 'name,brand'
|
22
|
+
},
|
23
|
+
'unique_index' => {
|
24
|
+
'by_price' => 'price'
|
25
|
+
}
|
26
|
+
}.to_json
|
27
|
+
end
|
9
28
|
end
|
data/spec/client_spec.rb
CHANGED
@@ -61,6 +61,16 @@ describe "Client" do
|
|
61
61
|
@s.docname(:md_copy) => @data)
|
62
62
|
end
|
63
63
|
|
64
|
+
it "should update client schema_sha1" do
|
65
|
+
set_state(@s.docname(:md_copy) => @data,
|
66
|
+
@s.docname(:schema_sha1) => 'foobar',
|
67
|
+
@c.docname(:cd) => {'foo' => {'bar' => 'abc'}})
|
68
|
+
@c.update_clientdoc([@s_fields[:name]])
|
69
|
+
verify_result(@c.docname(:cd) => @data,
|
70
|
+
@s.docname(:md_copy) => @data,
|
71
|
+
@c.docname(:schema_sha1) => 'foobar')
|
72
|
+
end
|
73
|
+
|
64
74
|
describe "Client Stats" do
|
65
75
|
|
66
76
|
before(:each) do
|
data/spec/client_sync_spec.rb
CHANGED
@@ -35,6 +35,19 @@ describe "ClientSync" do
|
|
35
35
|
@cs.client.docname(:cd) => data)
|
36
36
|
end
|
37
37
|
|
38
|
+
it "should handle send cud if pass_through is set" do
|
39
|
+
data = {'1'=>@product1,'2'=>@product2}
|
40
|
+
expected = {'insert'=>data}
|
41
|
+
set_test_data('test_db_storage',data)
|
42
|
+
@s.pass_through = 'true'
|
43
|
+
@cs.send_cud.should == [{'version'=>ClientSync::VERSION},
|
44
|
+
{'token'=>@c.get_value(:page_token)},
|
45
|
+
{'count'=>data.size},{'progress_count'=>data.size},
|
46
|
+
{'total_count'=>data.size},expected]
|
47
|
+
verify_result(@cs.client.docname(:page) => {},
|
48
|
+
@cs.client.docname(:cd) => {})
|
49
|
+
end
|
50
|
+
|
38
51
|
it "should return read errors in send cud" do
|
39
52
|
msg = "Error during query"
|
40
53
|
data = {'1'=>@product1,'2'=>@product2}
|
@@ -405,6 +418,113 @@ describe "ClientSync" do
|
|
405
418
|
Store.get_data(@cs.client.docname(:page)).should == {}
|
406
419
|
@c.get_value(:page_token).should be_nil
|
407
420
|
end
|
421
|
+
|
422
|
+
it "should send metadata with page" do
|
423
|
+
expected = {'1'=>@product1}
|
424
|
+
set_state('test_db_storage' => expected)
|
425
|
+
metadata = "{\"foo\":\"bar\"}"
|
426
|
+
mock_metadata_method([SampleAdapter]) do
|
427
|
+
result = @cs.send_cud
|
428
|
+
token = @c.get_value(:page_token)
|
429
|
+
result.should == [{"version"=>ClientSync::VERSION},{"token"=>token},
|
430
|
+
{"count"=>1}, {"progress_count"=>0},{"total_count"=>1},
|
431
|
+
{'metadata'=>metadata,'insert'=>expected}]
|
432
|
+
@c.get_value(:metadata_page).should == metadata
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
it "should send metadata with resend page" do
|
437
|
+
expected = {'1'=>@product1}
|
438
|
+
set_state('test_db_storage' => expected)
|
439
|
+
mock_metadata_method([SampleAdapter]) do
|
440
|
+
result = @cs.send_cud
|
441
|
+
token = @c.get_value(:page_token)
|
442
|
+
@cs.send_cud.should == [{"version"=>ClientSync::VERSION},{"token"=>token},
|
443
|
+
{"count"=>1}, {"progress_count"=>0},{"total_count"=>1},
|
444
|
+
{'metadata'=>"{\"foo\":\"bar\"}",'insert'=>expected}]
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
it "should ack metadata page with ack token" do
|
449
|
+
expected = {'1'=>@product1}
|
450
|
+
set_state('test_db_storage' => expected)
|
451
|
+
mock_metadata_method([SampleAdapter]) do
|
452
|
+
result = @cs.send_cud
|
453
|
+
token = @c.get_value(:page_token)
|
454
|
+
@cs.send_cud(token).should == [{"version"=>ClientSync::VERSION},{"token"=>""},
|
455
|
+
{"count"=>0}, {"progress_count"=>1},{"total_count"=>1},{}]
|
456
|
+
@c.get_value(:metadata_page).should be_nil
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
it "shouldn't send schema-changed if client schema sha1 is nil" do
|
461
|
+
expected = {'1'=>@product1}
|
462
|
+
set_state('test_db_storage' => expected)
|
463
|
+
mock_schema_method([SampleAdapter]) do
|
464
|
+
result = @cs.send_cud
|
465
|
+
token = @c.get_value(:page_token)
|
466
|
+
result.should == [{"version"=>ClientSync::VERSION},{"token"=>token},
|
467
|
+
{"count"=>1}, {"progress_count"=>0},{"total_count"=>1},{'insert'=>expected}]
|
468
|
+
@c.get_value(:schema_sha1).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
it "should send schema-changed instead of page" do
|
473
|
+
mock_schema_method([SampleAdapter]) do
|
474
|
+
@c.put_value(:schema_sha1,'foo')
|
475
|
+
result = @cs.send_cud
|
476
|
+
token = @c.get_value(:page_token)
|
477
|
+
result.should == [{"version"=>ClientSync::VERSION},{"token"=>token},
|
478
|
+
{"count"=>0}, {"progress_count"=>0},{"total_count"=>0},{'schema-changed'=>'true'}]
|
479
|
+
@c.get_value(:schema_page).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
|
480
|
+
@c.get_value(:schema_sha1).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
it "should re-send schema-changed if no token sent" do
|
485
|
+
mock_schema_method([SampleAdapter]) do
|
486
|
+
@c.put_value(:schema_sha1,'foo')
|
487
|
+
result = @cs.send_cud
|
488
|
+
token = @c.get_value(:page_token)
|
489
|
+
@cs.send_cud.should == [{"version"=>ClientSync::VERSION},{"token"=>token},
|
490
|
+
{"count"=>0}, {"progress_count"=>0},{"total_count"=>0},{'schema-changed'=>'true'}]
|
491
|
+
@c.get_value(:schema_page).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
|
492
|
+
@c.get_value(:schema_sha1).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
it "should ack schema-changed with token" do
|
497
|
+
mock_schema_method([SampleAdapter]) do
|
498
|
+
@c.put_value(:schema_sha1,'foo')
|
499
|
+
result = @cs.send_cud
|
500
|
+
token = @c.get_value(:page_token)
|
501
|
+
@cs.send_cud(token).should == [{"version"=>ClientSync::VERSION},{"token"=>""},
|
502
|
+
{"count"=>0}, {"progress_count"=>0},{"total_count"=>0},{}]
|
503
|
+
@c.get_value(:schema_page).should be_nil
|
504
|
+
@c.get_value(:schema_sha1).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
it "should expire bulk data if schema changed" do
|
509
|
+
docname = bulk_data_docname(@a.id,@u.id)
|
510
|
+
data = BulkData.create(:name => docname,
|
511
|
+
:state => :inprogress,
|
512
|
+
:app_id => @a.id,
|
513
|
+
:user_id => @u.id,
|
514
|
+
:sources => [@s_fields[:name]])
|
515
|
+
data.refresh_time = Time.now.to_i + 600
|
516
|
+
mock_schema_method([SampleAdapter]) do
|
517
|
+
@c.put_value(:schema_sha1,'foo')
|
518
|
+
result = @cs.send_cud
|
519
|
+
token = @c.get_value(:page_token)
|
520
|
+
@cs.send_cud(token).should == [{"version"=>ClientSync::VERSION},{"token"=>""},
|
521
|
+
{"count"=>0}, {"progress_count"=>0},{"total_count"=>0},{}]
|
522
|
+
@c.get_value(:schema_page).should be_nil
|
523
|
+
@c.get_value(:schema_sha1).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
|
524
|
+
data = BulkData.load(docname)
|
525
|
+
data.refresh_time.should <= Time.now.to_i
|
526
|
+
end
|
527
|
+
end
|
408
528
|
end
|
409
529
|
|
410
530
|
describe "bulk data" do
|
@@ -451,7 +571,6 @@ describe "ClientSync" do
|
|
451
571
|
:app_id => @a.id,
|
452
572
|
:user_id => name,
|
453
573
|
:sources => [@s_fields[:name]])
|
454
|
-
puts "data: #{data.inspect}"
|
455
574
|
BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,name))
|
456
575
|
data = BulkData.load(bulk_data_docname(@a.id,name))
|
457
576
|
data.url.should match /a%20b/
|
@@ -76,6 +76,25 @@ describe "BulkDataJob" do
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
+
it "should create sqlite data with source schema" do
|
80
|
+
set_state('test_db_storage' => @data)
|
81
|
+
mock_schema_method([SampleAdapter]) do
|
82
|
+
docname = bulk_data_docname(@a.id,@u.id)
|
83
|
+
data = BulkData.create(:name => docname,
|
84
|
+
:state => :inprogress,
|
85
|
+
:app_id => @a.id,
|
86
|
+
:user_id => @u.id,
|
87
|
+
:sources => [@s_fields[:name]])
|
88
|
+
BulkDataJob.perform("data_name" => data.name)
|
89
|
+
data = BulkData.load(docname)
|
90
|
+
data.completed?.should == true
|
91
|
+
verify_result(@s.docname(:md) => @data,
|
92
|
+
@s.docname(:schema) => "{\"property\":{\"brand\":\"string\",\"name\":\"string\"},\"version\":\"1.0\"}",
|
93
|
+
@s.docname(:md_copy) => @data)
|
94
|
+
validate_db(data,@s.name => @data).should == true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
79
98
|
it "should raise exception if hsqldata fails" do
|
80
99
|
data = BulkData.create(:name => bulk_data_docname(@a.id,@u.id),
|
81
100
|
:state => :inprogress,
|
data/spec/jobs/ping_job_spec.rb
CHANGED
@@ -32,4 +32,14 @@ describe "PingJob" do
|
|
32
32
|
lambda { PingJob.perform(params) }.should_not raise_error
|
33
33
|
end
|
34
34
|
|
35
|
+
it "should skip ping for empty device_pin" do
|
36
|
+
params = {"user_id" => @u.id, "api_token" => @api_token,
|
37
|
+
"sources" => [@s.name], "message" => 'hello world',
|
38
|
+
"vibrate" => '5', "badge" => '5', "sound" => 'hello.mp3'}
|
39
|
+
@c.device_type = 'blackberry'
|
40
|
+
@c.device_pin = nil
|
41
|
+
PingJob.should_receive(:log).once.with("Skipping ping for non-registered client_id '#{@c.id}'...")
|
42
|
+
lambda { PingJob.perform(params) }.should_not raise_error
|
43
|
+
end
|
44
|
+
|
35
45
|
end
|
data/spec/ping/android_spec.rb
CHANGED
@@ -42,4 +42,14 @@ describe "Ping Android" do
|
|
42
42
|
actual['collapse_key'] = "RAND_KEY" unless actual['collapse_key'].nil?
|
43
43
|
actual.should == expected
|
44
44
|
end
|
45
|
+
|
46
|
+
it "should trim empty or nil params from c2d_message" do
|
47
|
+
expected = {'registration_id' => @c.device_pin, 'collapse_key' => "RAND_KEY",
|
48
|
+
'data.vibrate' => '5', 'data.do_sync' => '', 'data.sound' => "hello.mp3"}
|
49
|
+
params = {"device_pin" => @c.device_pin,
|
50
|
+
"sources" => [], "message" => '', "vibrate" => '5', "sound" => 'hello.mp3'}
|
51
|
+
actual = Android.c2d_message(params)
|
52
|
+
actual['collapse_key'] = "RAND_KEY" unless actual['collapse_key'].nil?
|
53
|
+
actual.should == expected
|
54
|
+
end
|
45
55
|
end
|
data/spec/rhosync_spec.rb
CHANGED
@@ -43,6 +43,18 @@ describe "Rhosync" do
|
|
43
43
|
App.load(@test_app_name).sources.members.should == []
|
44
44
|
end
|
45
45
|
|
46
|
+
it "should exit if schema config exists" do
|
47
|
+
config = Rhosync.get_config(get_testapp_path)
|
48
|
+
config[:sources]['FixedSchemaAdapter'].merge!(
|
49
|
+
'schema' => {'property' => 'foo'}
|
50
|
+
)
|
51
|
+
Rhosync.stub!(:get_config).and_return(config)
|
52
|
+
Rhosync.should_receive(:log).once.with(
|
53
|
+
"ERROR: 'schema' field in settings.yml is not supported anymore, please use source adapter schema method!"
|
54
|
+
)
|
55
|
+
lambda { Rhosync.bootstrap(get_testapp_path) }.should raise_error(SystemExit)
|
56
|
+
end
|
57
|
+
|
46
58
|
it "should add associations during bootstrap" do
|
47
59
|
Rhosync.bootstrap(get_testapp_path)
|
48
60
|
s = Source.load('SampleAdapter',{:app_id => @test_app_name,:user_id => '*'})
|
data/spec/server/server_spec.rb
CHANGED
@@ -12,9 +12,7 @@ describe "Server" do
|
|
12
12
|
|
13
13
|
include Rack::Test::Methods
|
14
14
|
include Rhosync
|
15
|
-
|
16
|
-
it_should_behave_like "DBObjectsHelper"
|
17
|
-
|
15
|
+
|
18
16
|
before(:each) do
|
19
17
|
require File.join(get_testapp_path,@test_app_name)
|
20
18
|
Rhosync.bootstrap(get_testapp_path) do |rhosync|
|
@@ -28,6 +26,8 @@ describe "Server" do
|
|
28
26
|
Rhosync::Server.use Rack::Static, :urls => ["/data"],
|
29
27
|
:root => File.expand_path(File.join(File.dirname(__FILE__),'..','apps','rhotestapp'))
|
30
28
|
end
|
29
|
+
|
30
|
+
it_should_behave_like "DBObjectsHelper"
|
31
31
|
|
32
32
|
def app
|
33
33
|
@app ||= Rhosync::Server.new
|
@@ -127,17 +127,7 @@ describe "Server" do
|
|
127
127
|
@source_config = {
|
128
128
|
"sources"=>
|
129
129
|
{"FixedSchemaAdapter"=>
|
130
|
-
{"
|
131
|
-
{"property"=>
|
132
|
-
{"image_url_cropped"=>"blob,overwrite",
|
133
|
-
"price"=>"string",
|
134
|
-
"brand"=>"string",
|
135
|
-
"name"=>"string",
|
136
|
-
"image_url"=>"blob"},
|
137
|
-
"unique_index"=>{"by_price"=>"price"},
|
138
|
-
"version"=>"1.0",
|
139
|
-
"index"=>{"by_name_brand"=>"name,brand"}},
|
140
|
-
"poll_interval"=>300,
|
130
|
+
{"poll_interval"=>300,
|
141
131
|
"sync_type"=>"incremental",
|
142
132
|
"belongs_to"=>[{"brand"=>"SampleAdapter"}]},
|
143
133
|
"SampleAdapter"=>{"poll_interval"=>300},
|
@@ -148,7 +138,7 @@ describe "Server" do
|
|
148
138
|
it "should respond to clientcreate" do
|
149
139
|
get "/application/clientcreate?device_type=blackberry"
|
150
140
|
last_response.should be_ok
|
151
|
-
last_response.content_type.should
|
141
|
+
last_response.content_type.should =~ /application\/json/
|
152
142
|
id = JSON.parse(last_response.body)['client']['client_id']
|
153
143
|
id.length.should == 32
|
154
144
|
JSON.parse(last_response.body).should ==
|
@@ -235,7 +225,7 @@ describe "Server" do
|
|
235
225
|
set_test_data('test_db_storage',data)
|
236
226
|
get "/application",:client_id => @c.id,:source_name => @s.name,:version => ClientSync::VERSION
|
237
227
|
last_response.should be_ok
|
238
|
-
last_response.content_type.should
|
228
|
+
last_response.content_type.should =~ /application\/json/
|
239
229
|
token = @c.get_value(:page_token)
|
240
230
|
JSON.parse(last_response.body).should == [{"version"=>ClientSync::VERSION},{"token"=>token},
|
241
231
|
{"count"=>2}, {"progress_count"=>0},{"total_count"=>2},{'insert'=>data}]
|
@@ -257,6 +247,12 @@ describe "Server" do
|
|
257
247
|
{"count"=>0}, {"progress_count"=>2}, {"total_count"=>2},{}]
|
258
248
|
end
|
259
249
|
|
250
|
+
it "should return error if source_name is unknown" do
|
251
|
+
get "/application",:client_id => @c.id,:source_name => 'Broken',:version => ClientSync::VERSION
|
252
|
+
last_response.status.should == 500
|
253
|
+
last_response.body.should == "ERROR: Source 'Broken' requested by client doesn't exist.\n"
|
254
|
+
end
|
255
|
+
|
260
256
|
it "should get deletes json" do
|
261
257
|
cs = ClientSync.new(@s,@c,1)
|
262
258
|
data = {'1'=>@product1,'2'=>@product2}
|
@@ -286,7 +282,7 @@ describe "Server" do
|
|
286
282
|
params = {:client_id => @c.id,:sources => sources,:search => {'name' => 'iPhone'},
|
287
283
|
:version => ClientSync::VERSION}
|
288
284
|
get "/application/search",params
|
289
|
-
last_response.content_type.should
|
285
|
+
last_response.content_type.should =~ /application\/json/
|
290
286
|
token = @c.get_value(:search_token)
|
291
287
|
JSON.parse(last_response.body).should == [[{'version'=>ClientSync::VERSION},{'token'=>token},
|
292
288
|
{'source'=>sources[0][:name]},{'count'=>1},{'insert'=>{'1'=>@product1}}]]
|
data/spec/source_sync_spec.rb
CHANGED
@@ -72,6 +72,17 @@ describe "SourceSync" do
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
+
it "should process source adapter schema" do
|
76
|
+
mock_schema_method([SampleAdapter]) do
|
77
|
+
expected = {'1'=>@product1,'2'=>@product2}
|
78
|
+
set_state('test_db_storage' => expected)
|
79
|
+
@ss.process_query
|
80
|
+
verify_result(@s.docname(:md) => expected,
|
81
|
+
@s.docname(:schema) => "{\"property\":{\"brand\":\"string\",\"name\":\"string\"},\"version\":\"1.0\"}",
|
82
|
+
@s.docname(:schema_sha1) => "8c148c8c1a66c7baf685c07d58bea360da87981b")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
75
86
|
it "should process source adapter with stash" do
|
76
87
|
expected = {'1'=>@product1,'2'=>@product2}
|
77
88
|
set_state('test_db_storage' => expected)
|
@@ -94,6 +105,16 @@ describe "SourceSync" do
|
|
94
105
|
end
|
95
106
|
end
|
96
107
|
|
108
|
+
it "should process source adapter with pass_through set" do
|
109
|
+
expected = {'1'=>@product1,'2'=>@product2}
|
110
|
+
set_state('test_db_storage' => expected)
|
111
|
+
@s.pass_through = 'true'
|
112
|
+
@ss.process_query.should == expected
|
113
|
+
verify_result(@s.docname(:md) => {},
|
114
|
+
@s.docname(:md_size) => nil)
|
115
|
+
@s.pass_through = nil
|
116
|
+
end
|
117
|
+
|
97
118
|
describe "create" do
|
98
119
|
it "should do create where adapter.create returns nil" do
|
99
120
|
set_state(@c.docname(:create) => {'2'=>@product2})
|
@@ -219,7 +240,7 @@ describe "SourceSync" do
|
|
219
240
|
verify_result(@s.docname(:md) => expected,
|
220
241
|
@s.docname(:errors) => {})
|
221
242
|
else
|
222
|
-
@ss.search(@c.id).should ==
|
243
|
+
@ss.search(@c.id).should == expected
|
223
244
|
verify_result(@c.docname(:search) => expected,
|
224
245
|
@c.docname(:search_errors) => {})
|
225
246
|
end
|
@@ -230,11 +251,11 @@ describe "SourceSync" do
|
|
230
251
|
@ss.should_receive(:log).with("SourceAdapter raised #{operation} exception: #{msg}")
|
231
252
|
set_test_data('test_db_storage',{},msg,"#{operation} error")
|
232
253
|
if operation == 'query'
|
233
|
-
@ss.read.should
|
254
|
+
@ss.read.should be_nil
|
234
255
|
verify_result(@s.docname(:md) => {},
|
235
256
|
@s.docname(:errors) => {'query-error'=>{'message'=>msg}})
|
236
257
|
else
|
237
|
-
@ss.search(@c.id).should
|
258
|
+
@ss.search(@c.id).should be_nil
|
238
259
|
verify_result(@c.docname(:search) => {},
|
239
260
|
@c.docname(:search_errors) => {'search-error'=>{'message'=>msg}})
|
240
261
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -115,7 +115,7 @@ module TestHelpers
|
|
115
115
|
|
116
116
|
def validate_db_by_name(db,s,data)
|
117
117
|
db.execute("select source_id,name,sync_priority,partition,
|
118
|
-
sync_type,source_attribs,metadata,blob_attribs,associations
|
118
|
+
sync_type,source_attribs,metadata,schema,blob_attribs,associations
|
119
119
|
from sources where name='#{s.name}'").each do |row|
|
120
120
|
return false if row[0].to_s != s.source_id.to_s
|
121
121
|
return false if row[1] != s.name
|
@@ -124,8 +124,9 @@ module TestHelpers
|
|
124
124
|
return false if row[4] != s.sync_type.to_s
|
125
125
|
return false if row[5] != (s.schema ? "" : get_attrib_counter(data))
|
126
126
|
return false if row[6] != s.get_value(:metadata)
|
127
|
-
return false if row[7] != s.
|
128
|
-
return false if row[8] != s.
|
127
|
+
return false if row[7] != s.schema
|
128
|
+
return false if row[8] != s.blob_attribs
|
129
|
+
return false if row[9] != s.has_many
|
129
130
|
end
|
130
131
|
|
131
132
|
data = json_clone(data)
|
@@ -174,6 +175,24 @@ module TestHelpers
|
|
174
175
|
end
|
175
176
|
end
|
176
177
|
|
178
|
+
def mock_schema_method(adapters, &block)
|
179
|
+
adapters.each do |klass|
|
180
|
+
klass.class_eval 'def schema
|
181
|
+
{
|
182
|
+
"property" => {
|
183
|
+
"name" => "string",
|
184
|
+
"brand" => "string"
|
185
|
+
},
|
186
|
+
"version" => "1.0"
|
187
|
+
}.to_json
|
188
|
+
end'
|
189
|
+
end
|
190
|
+
yield
|
191
|
+
adapters.each do |klass|
|
192
|
+
klass.class_eval "def schema; end"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
177
196
|
def unzip_file(file,file_dir)
|
178
197
|
Zip::ZipFile.open(file) do |zip_file|
|
179
198
|
zip_file.each do |f|
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rhosync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 62196471
|
5
5
|
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 1
|
9
9
|
- 0
|
10
10
|
- beta
|
11
|
-
-
|
12
|
-
version: 2.1.0.beta.
|
11
|
+
- 2
|
12
|
+
version: 2.1.0.beta.2
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Rhomobile
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2010-
|
20
|
+
date: 2010-12-07 00:00:00 -08:00
|
21
21
|
default_executable: rhosync
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|
@@ -156,11 +156,11 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - ~>
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
hash:
|
159
|
+
hash: 13
|
160
160
|
segments:
|
161
161
|
- 1
|
162
|
-
-
|
163
|
-
version: "1.
|
162
|
+
- 1
|
163
|
+
version: "1.1"
|
164
164
|
type: :runtime
|
165
165
|
version_requirements: *id009
|
166
166
|
- !ruby/object:Gem::Dependency
|
@@ -551,8 +551,8 @@ homepage: http://rhomobile.com/products/rhosync
|
|
551
551
|
licenses: []
|
552
552
|
|
553
553
|
post_install_message:
|
554
|
-
rdoc_options:
|
555
|
-
|
554
|
+
rdoc_options: []
|
555
|
+
|
556
556
|
require_paths:
|
557
557
|
- lib
|
558
558
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -583,6 +583,9 @@ signing_key:
|
|
583
583
|
specification_version: 3
|
584
584
|
summary: RhoSync Synchronization Framework
|
585
585
|
test_files:
|
586
|
+
- examples/simple/application.rb
|
587
|
+
- examples/simple/sources/sample_adapter.rb
|
588
|
+
- examples/simple/sources/simple_adapter.rb
|
586
589
|
- spec/api/api_helper.rb
|
587
590
|
- spec/api/create_client_spec.rb
|
588
591
|
- spec/api/create_user_spec.rb
|
@@ -616,8 +619,8 @@ test_files:
|
|
616
619
|
- spec/apps/rhotestapp/sources/sample_adapter.rb
|
617
620
|
- spec/apps/rhotestapp/sources/simple_adapter.rb
|
618
621
|
- spec/apps/rhotestapp/sources/sub_adapter.rb
|
619
|
-
- spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem/mygem.rb
|
620
622
|
- spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem.rb
|
623
|
+
- spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem/mygem.rb
|
621
624
|
- spec/bulk_data/bulk_data_spec.rb
|
622
625
|
- spec/client_spec.rb
|
623
626
|
- spec/client_sync_spec.rb
|
@@ -649,6 +652,3 @@ test_files:
|
|
649
652
|
- spec/sync_states_spec.rb
|
650
653
|
- spec/test_methods_spec.rb
|
651
654
|
- spec/user_spec.rb
|
652
|
-
- examples/simple/application.rb
|
653
|
-
- examples/simple/sources/sample_adapter.rb
|
654
|
-
- examples/simple/sources/simple_adapter.rb
|