rhosync 2.1.0.beta.1 → 2.1.0.beta.2
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 +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
|