rhosync 2.0.0.beta3 → 2.0.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/protocol.html +87 -87
- data/lib/rhosync/jobs/bulk_data_job.rb +51 -2
- data/lib/rhosync/server.rb +15 -13
- data/lib/rhosync/source.rb +3 -0
- data/lib/rhosync/test_methods.rb +78 -0
- data/lib/rhosync/version.rb +1 -1
- data/lib/rhosync.rb +4 -0
- data/spec/api/get_source_params_spec.rb +2 -1
- data/spec/api/list_sources_spec.rb +3 -3
- data/spec/apps/rhotestapp/settings/settings.yml +13 -0
- data/spec/apps/rhotestapp/sources/fixed_schema_adapter.rb +9 -0
- data/spec/doc/doc_spec.rb +0 -2
- data/spec/jobs/bulk_data_job_spec.rb +10 -6
- data/spec/server/server_spec.rb +32 -7
- data/spec/spec_helper.rb +51 -17
- metadata +5 -3
@@ -48,6 +48,50 @@ module Rhosync
|
|
48
48
|
end
|
49
49
|
counter
|
50
50
|
end
|
51
|
+
|
52
|
+
# Loads data into fixed schema table based on source settings
|
53
|
+
def self.import_data_to_fixed_schema(db,source)
|
54
|
+
data = source.get_data(:md)
|
55
|
+
counter = {}
|
56
|
+
columns,qm = [],[]
|
57
|
+
create_table = ['object varchar']
|
58
|
+
schema = JSON.parse(source.schema)
|
59
|
+
|
60
|
+
db.transaction do |database|
|
61
|
+
# Create a table with columns specified by 'property' array in settings
|
62
|
+
schema['property'].each do |column|
|
63
|
+
create_table << "#{column.keys[0]} varchar default NULL"
|
64
|
+
columns << column.keys[0]
|
65
|
+
qm << '?'
|
66
|
+
end
|
67
|
+
database.execute("CREATE TABLE #{source.name}(
|
68
|
+
#{create_table.join(",")} );")
|
69
|
+
|
70
|
+
# Insert each object as single row in fixed schema table
|
71
|
+
database.prepare("insert into #{source.name}
|
72
|
+
(object,#{columns.join(',')}) values (?,#{qm.join(',')})") do |stmt|
|
73
|
+
data.each do |obj,row|
|
74
|
+
args = [obj]
|
75
|
+
columns.each do |col|
|
76
|
+
args << row[col]
|
77
|
+
end
|
78
|
+
stmt.execute(args)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Create indexes for specified columns in settings 'index'
|
83
|
+
schema['index'].each do |index|
|
84
|
+
database.execute("CREATE INDEX #{index.keys[0]} on #{source.name} (#{index.values[0]});")
|
85
|
+
end if schema['index']
|
86
|
+
|
87
|
+
# Create unique indexes for specified columns in settings 'unique_index'
|
88
|
+
schema['unique_index'].each do |index|
|
89
|
+
database.execute("CREATE UNIQUE INDEX #{index.keys[0]} on #{source.name} (#{index.values[0]});")
|
90
|
+
end if schema['unique_index']
|
91
|
+
end
|
92
|
+
|
93
|
+
return {}
|
94
|
+
end
|
51
95
|
|
52
96
|
def self.refs_to_s(refs)
|
53
97
|
str = ''
|
@@ -84,7 +128,12 @@ module Rhosync
|
|
84
128
|
:user_id => bulk_data.user_id})
|
85
129
|
source.source_id = src_counter
|
86
130
|
src_counter += 1
|
87
|
-
source_attrib_refs =
|
131
|
+
source_attrib_refs = nil
|
132
|
+
if source.schema
|
133
|
+
source_attrib_refs = import_data_to_fixed_schema(db,source)
|
134
|
+
else
|
135
|
+
source_attrib_refs = import_data_to_object_values(db,source)
|
136
|
+
end
|
88
137
|
sources_refs[source_name] =
|
89
138
|
{:source => source, :refs => source_attrib_refs}
|
90
139
|
lap_timer("finished importing sqlite data for #{source_name}",timer)
|
@@ -99,7 +148,7 @@ module Rhosync
|
|
99
148
|
hsql_file = dbfile + ".hsqldb"
|
100
149
|
raise Exception.new("Error running hsqldata") unless
|
101
150
|
system('java','-cp', File.join(File.dirname(__FILE__),'..','..','..','vendor','hsqldata.jar'),
|
102
|
-
'com.rhomobile.hsqldata.HsqlData', dbfile, hsql_file
|
151
|
+
'com.rhomobile.hsqldata.HsqlData', dbfile, hsql_file)
|
103
152
|
end
|
104
153
|
|
105
154
|
def self.get_file_args(bulk_data_name,ts)
|
data/lib/rhosync/server.rb
CHANGED
@@ -112,10 +112,6 @@ module Rhosync
|
|
112
112
|
params[:source_name] ? {:source_name => current_source.name} : {:source_name => '*'})
|
113
113
|
end
|
114
114
|
end
|
115
|
-
|
116
|
-
def source_config
|
117
|
-
{ "sources" => Rhosync.get_config(Rhosync.base_directory)[:sources] }
|
118
|
-
end
|
119
115
|
|
120
116
|
def catch_all
|
121
117
|
begin
|
@@ -136,15 +132,21 @@ module Rhosync
|
|
136
132
|
Rhosync.log "Rhosync Server v#{Rhosync::VERSION} started..."
|
137
133
|
|
138
134
|
before do
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
135
|
+
begin
|
136
|
+
if params["cud"]
|
137
|
+
cud = JSON.parse(params["cud"])
|
138
|
+
params.delete("cud")
|
139
|
+
params.merge!(cud)
|
140
|
+
end
|
141
|
+
if request.env['CONTENT_TYPE'] == 'application/json'
|
142
|
+
params.merge!(JSON.parse(request.body.read))
|
143
|
+
request.body.rewind
|
144
|
+
end
|
145
|
+
rescue JSON::ParserError => jpe
|
146
|
+
throw :halt, [500, "Server error while processing client data"]
|
147
|
+
rescue Exception => e
|
148
|
+
throw :halt, [500, "Internal server error"]
|
149
|
+
end
|
148
150
|
if params[:version] and params[:version].to_i < 3
|
149
151
|
throw :halt, [404, "Server supports version 3 or higher of the protocol."]
|
150
152
|
end
|
data/lib/rhosync/source.rb
CHANGED
@@ -13,6 +13,7 @@ module Rhosync
|
|
13
13
|
field :queue,:string
|
14
14
|
field :query_queue,:string
|
15
15
|
field :cud_queue,:string
|
16
|
+
field :schema, :string
|
16
17
|
attr_accessor :app_id, :user_id
|
17
18
|
validates_presence_of :name #, :source_id
|
18
19
|
|
@@ -27,6 +28,7 @@ module Rhosync
|
|
27
28
|
fields[:partition_type] ||= :user
|
28
29
|
fields[:poll_interval] ||= 300
|
29
30
|
fields[:sync_type] ||= :incremental
|
31
|
+
fields[:schema] = fields[:schema].to_json if fields[:schema]
|
30
32
|
end
|
31
33
|
|
32
34
|
def self.create(fields,params)
|
@@ -43,6 +45,7 @@ module Rhosync
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def update(fields)
|
48
|
+
fields = fields.with_indifferent_access # so we can access hash keys as symbols
|
46
49
|
self.class.set_defaults(fields)
|
47
50
|
super(fields)
|
48
51
|
end
|
data/lib/rhosync/test_methods.rb
CHANGED
@@ -4,6 +4,8 @@ module Rhosync
|
|
4
4
|
end
|
5
5
|
|
6
6
|
module TestMethods
|
7
|
+
# Initializes the source adapter under test for a given user, typically in a before(:each) block
|
8
|
+
# setup_test_for(Product,'testuser') #=> 'testuser' will be used by rest of the specs
|
7
9
|
def setup_test_for(adapter,user_id)
|
8
10
|
app_id = 'application'
|
9
11
|
s_fields = {
|
@@ -23,15 +25,51 @@ module Rhosync
|
|
23
25
|
@ss = SourceSync.new(@s)
|
24
26
|
end
|
25
27
|
|
28
|
+
# Executes the adapter's query method and returns
|
29
|
+
# the master document (:md) stored in redis
|
30
|
+
# For example, if your source adapter query method was:
|
31
|
+
# def query(params=nil)
|
32
|
+
# @result = {
|
33
|
+
# "1"=>{"name"=>"Acme", "industry"=>"Electronics"},
|
34
|
+
# "2"=>{"name"=>"Best", "industry"=>"Software"}
|
35
|
+
# }
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# test_query would return:
|
39
|
+
# {
|
40
|
+
# "1"=>{"name"=>"Acme", "industry"=>"Electronics"},
|
41
|
+
# "2"=>{"name"=>"Best", "industry"=>"Software"}
|
42
|
+
# }
|
26
43
|
def test_query
|
27
44
|
@ss.process_query
|
28
45
|
return md
|
29
46
|
end
|
30
47
|
|
48
|
+
# Returns any errors stored in redis for the previous source adapter query
|
49
|
+
# For example: {"query-error"=>{"message"=>"error connecting to web service!"}}
|
31
50
|
def query_errors
|
32
51
|
@s.get_data(:errors)
|
33
52
|
end
|
34
53
|
|
54
|
+
# Execute's the adapter's create method with a provided record and
|
55
|
+
# returns the object string from the create method. If the create method
|
56
|
+
# returns a string, then a link will be saved for the device next time
|
57
|
+
# it synchronizes. This link can be tested here.
|
58
|
+
#
|
59
|
+
# For example, in your spec:
|
60
|
+
# @product = {
|
61
|
+
# 'name' => 'iPhone',
|
62
|
+
# 'brand' => 'Apple',
|
63
|
+
# 'price' => '$299.99',
|
64
|
+
# 'quantity' => '5',
|
65
|
+
# 'sku' => '1234'
|
66
|
+
# }
|
67
|
+
# new_product_id = test_create(@product)
|
68
|
+
# create_errors.should == {}
|
69
|
+
# md[new_product_id].should == @product
|
70
|
+
#
|
71
|
+
# This will return the result of the adapter's create method. The master
|
72
|
+
# document (:md) should also contain the new record.
|
35
73
|
def test_create(record)
|
36
74
|
@c.put_data(:create,{'temp-id' => record})
|
37
75
|
@ss.create(@c.id)
|
@@ -39,32 +77,72 @@ module Rhosync
|
|
39
77
|
links ? links['l'] : nil
|
40
78
|
end
|
41
79
|
|
80
|
+
# Returns any errors stored in redis from the previous source adapter create
|
81
|
+
# (same structure as query errors)
|
42
82
|
def create_errors
|
43
83
|
@c.get_data(:create_errors)
|
44
84
|
end
|
45
85
|
|
86
|
+
# Execute the source adapter's update method.
|
87
|
+
# Takes a record as hash of hashes (object_id => object)
|
88
|
+
#
|
89
|
+
# For example:
|
90
|
+
# test_update({'4' => {'price' => '$199.99'}})
|
91
|
+
# update_errors.should == {}
|
92
|
+
# test_query
|
93
|
+
# md[product_id]['price'].should == '$199.99'
|
94
|
+
#
|
95
|
+
# This will call the adapter's update method for object_id '4'
|
96
|
+
# NOTE: To test the master document, you will need to run def test_query
|
97
|
+
# as shown above
|
46
98
|
def test_update(record)
|
47
99
|
@c.put_data(:update,record)
|
48
100
|
@ss.update(@c.id)
|
49
101
|
end
|
50
102
|
|
103
|
+
# Returns any errors stored in redis from the previous source adapter update
|
104
|
+
# (same structure as query errors)
|
51
105
|
def update_errors
|
52
106
|
@c.get_data(:update_errors)
|
53
107
|
end
|
54
108
|
|
109
|
+
# Execute the source adapter's delete method.
|
110
|
+
# Takes a record as hash of hashes (object_id => object)
|
111
|
+
#
|
112
|
+
# For example:
|
113
|
+
# @product = {
|
114
|
+
# 'name' => 'iPhone',
|
115
|
+
# 'brand' => 'Apple',
|
116
|
+
# 'price' => '$299.99',
|
117
|
+
# 'quantity' => '5',
|
118
|
+
# 'sku' => '1234'
|
119
|
+
# }
|
120
|
+
# test_delete('4' => @product)
|
121
|
+
# delete_errors.should == {}
|
122
|
+
# md.should == {}
|
123
|
+
#
|
124
|
+
# This will call the adapter's delete method for product '4'
|
125
|
+
# NOTE: The master document (:md) will be updated and can be
|
126
|
+
# verified as shown above.
|
55
127
|
def test_delete(record)
|
56
128
|
@c.put_data(:delete,record)
|
57
129
|
@ss.delete(@c.id)
|
58
130
|
end
|
59
131
|
|
132
|
+
# Returns any errors stored in redis from the previous source adapter delete
|
133
|
+
# (same structure as query errors)
|
60
134
|
def delete_errors
|
61
135
|
@c.get_data(:delete_errors)
|
62
136
|
end
|
63
137
|
|
138
|
+
# Returns the master document (:md) for the source adapter stored in redis.
|
139
|
+
# This is equivalent to the @result hash of hashes structure.
|
64
140
|
def md
|
65
141
|
@s.get_data(:md)
|
66
142
|
end
|
67
143
|
|
144
|
+
# Returns the client document (:cd) for the source adapter + client under test.
|
145
|
+
# The master document (:md) and client document (:cd) should be equal
|
68
146
|
def cd
|
69
147
|
@c.get_data(:cd)
|
70
148
|
end
|
data/lib/rhosync/version.rb
CHANGED
data/lib/rhosync.rb
CHANGED
@@ -122,6 +122,10 @@ module Rhosync
|
|
122
122
|
settings_file = File.join(basedir,'settings','settings.yml') if basedir
|
123
123
|
YAML.load_file(settings_file) if settings_file and File.exist?(settings_file)
|
124
124
|
end
|
125
|
+
|
126
|
+
def source_config
|
127
|
+
{ "sources" => Rhosync.get_config(Rhosync.base_directory)[:sources] }
|
128
|
+
end
|
125
129
|
### End Rhosync setup methods
|
126
130
|
|
127
131
|
|
@@ -19,7 +19,8 @@ describe "RhosyncApiGetSourceParams" do
|
|
19
19
|
{"name"=>"sync_type", "value"=>"incremental", "type"=>"string"},
|
20
20
|
{"name"=>"queue", "value"=>nil, "type"=>"string"},
|
21
21
|
{"name"=>"query_queue", "value"=>nil, "type"=>"string"},
|
22
|
-
{"name"=>"cud_queue", "value"=>nil, "type"=>"string"}
|
22
|
+
{"name"=>"cud_queue", "value"=>nil, "type"=>"string"},
|
23
|
+
{"name"=>"schema", "value"=>nil, "type"=>"string"}]
|
23
24
|
end
|
24
25
|
|
25
26
|
end
|
@@ -5,13 +5,13 @@ describe "RhosyncApiListSources" do
|
|
5
5
|
|
6
6
|
it "should list all application sources" do
|
7
7
|
post "/api/list_sources", {:api_token => @api_token}
|
8
|
-
JSON.parse(last_response.body).should == ["SimpleAdapter", "SampleAdapter"]
|
8
|
+
JSON.parse(last_response.body).sort.should == ["SimpleAdapter", "SampleAdapter", "FixedSchemaAdapter"].sort
|
9
9
|
end
|
10
10
|
|
11
11
|
it "should list all application sources using partition_type param" do
|
12
12
|
post "/api/list_sources",
|
13
13
|
{:api_token => @api_token, :partition_type => 'all'}
|
14
|
-
JSON.parse(last_response.body).should == ["SimpleAdapter", "SampleAdapter"]
|
14
|
+
JSON.parse(last_response.body).sort.should == ["SimpleAdapter", "SampleAdapter", "FixedSchemaAdapter"].sort
|
15
15
|
end
|
16
16
|
|
17
17
|
it "should list app partition sources" do
|
@@ -21,7 +21,7 @@ describe "RhosyncApiListSources" do
|
|
21
21
|
|
22
22
|
it "should list user partition sources" do
|
23
23
|
post "/api/list_sources", {:api_token => @api_token, :partition_type => :user}
|
24
|
-
JSON.parse(last_response.body).should == ["SampleAdapter"]
|
24
|
+
JSON.parse(last_response.body).sort.should == ["SampleAdapter", "FixedSchemaAdapter"].sort
|
25
25
|
end
|
26
26
|
|
27
27
|
end
|
@@ -4,6 +4,19 @@
|
|
4
4
|
SimpleAdapter:
|
5
5
|
poll_interval: 600
|
6
6
|
partition_type: app
|
7
|
+
FixedSchemaAdapter:
|
8
|
+
poll_interval: 300
|
9
|
+
schema:
|
10
|
+
version: '1.0'
|
11
|
+
property:
|
12
|
+
- name: string
|
13
|
+
- brand: string
|
14
|
+
- price: string
|
15
|
+
- image_url: blob
|
16
|
+
index:
|
17
|
+
- by_name_brand: 'name,brand'
|
18
|
+
unique_index:
|
19
|
+
- by_price: 'price'
|
7
20
|
|
8
21
|
:development:
|
9
22
|
:licensefile: settings/license.key
|
data/spec/doc/doc_spec.rb
CHANGED
@@ -16,16 +16,19 @@ describe "BulkDataJob" do
|
|
16
16
|
it "should create sqlite data file from master document" do
|
17
17
|
set_state('test_db_storage' => @data)
|
18
18
|
docname = bulk_data_docname(@a.id,@u.id)
|
19
|
+
expected = { @s_fields[:name] => @data,
|
20
|
+
'FixedSchemaAdapter' => @data
|
21
|
+
}
|
19
22
|
data = BulkData.create(:name => docname,
|
20
23
|
:state => :inprogress,
|
21
24
|
:app_id => @a.id,
|
22
25
|
:user_id => @u.id,
|
23
|
-
:sources => [@s_fields[:name]])
|
26
|
+
:sources => [@s_fields[:name], 'FixedSchemaAdapter'])
|
24
27
|
BulkDataJob.perform("data_name" => data.name)
|
25
28
|
data = BulkData.load(docname)
|
26
29
|
data.completed?.should == true
|
27
30
|
verify_result(@s.docname(:md) => @data,@s.docname(:md_copy) => @data)
|
28
|
-
validate_db(data
|
31
|
+
validate_db(data,expected).should == true
|
29
32
|
File.exists?(data.dbfile+'.rzip').should == true
|
30
33
|
File.exists?(data.dbfile+'.hsqldb.data').should == true
|
31
34
|
File.exists?(data.dbfile+'.hsqldb.script').should == true
|
@@ -33,7 +36,8 @@ describe "BulkDataJob" do
|
|
33
36
|
path = File.join(File.dirname(data.dbfile),'tmp')
|
34
37
|
FileUtils.mkdir_p path
|
35
38
|
unzip_file("#{data.dbfile}.rzip",path)
|
36
|
-
|
39
|
+
data.dbfile = File.join(path,File.basename(data.dbfile))
|
40
|
+
validate_db(data,expected).should == true
|
37
41
|
end
|
38
42
|
|
39
43
|
it "should not create hsql db files if blackberry_bulk_sync is disabled" do
|
@@ -49,11 +53,11 @@ describe "BulkDataJob" do
|
|
49
53
|
data = BulkData.load(docname)
|
50
54
|
data.completed?.should == true
|
51
55
|
verify_result(@s.docname(:md) => @data,@s.docname(:md_copy) => @data)
|
52
|
-
validate_db(data,@data).should == true
|
56
|
+
validate_db(data,@s.name => @data).should == true
|
53
57
|
File.exists?(data.dbfile+'.hsqldb.script').should == false
|
54
58
|
File.exists?(data.dbfile+'.hsqldb.properties').should == false
|
55
59
|
end
|
56
|
-
|
60
|
+
|
57
61
|
it "should create sqlite data with source metadata" do
|
58
62
|
set_state('test_db_storage' => @data)
|
59
63
|
mock_metadata_method([SampleAdapter]) do
|
@@ -69,7 +73,7 @@ describe "BulkDataJob" do
|
|
69
73
|
verify_result(@s.docname(:md) => @data,
|
70
74
|
@s.docname(:metadata) => {'foo'=>'bar'}.to_json,
|
71
75
|
@s.docname(:md_copy) => @data)
|
72
|
-
validate_db(data,@data).should == true
|
76
|
+
validate_db(data,@s.name => @data).should == true
|
73
77
|
end
|
74
78
|
end
|
75
79
|
|
data/spec/server/server_spec.rb
CHANGED
@@ -13,6 +13,8 @@ describe "Server" do
|
|
13
13
|
include Rack::Test::Methods
|
14
14
|
include Rhosync
|
15
15
|
|
16
|
+
it_should_behave_like "DBObjectsHelper"
|
17
|
+
|
16
18
|
before(:each) do
|
17
19
|
require File.join(get_testapp_path,@test_app_name)
|
18
20
|
Rhosync.bootstrap(get_testapp_path) do |rhosync|
|
@@ -24,14 +26,12 @@ describe "Server" do
|
|
24
26
|
:secret => "secure!"
|
25
27
|
)
|
26
28
|
Server.use Rack::Static, :urls => ["/data"],
|
27
|
-
:root => File.join(File.dirname(__FILE__),'..','apps','rhotestapp')
|
29
|
+
:root => File.expand_path(File.join(File.dirname(__FILE__),'..','apps','rhotestapp'))
|
28
30
|
end
|
29
31
|
|
30
32
|
def app
|
31
33
|
@app ||= Server.new
|
32
34
|
end
|
33
|
-
|
34
|
-
it_should_behave_like "DBObjectsHelper"
|
35
35
|
|
36
36
|
it "should show status page" do
|
37
37
|
get '/'
|
@@ -116,8 +116,19 @@ describe "Server" do
|
|
116
116
|
describe "client management routes" do
|
117
117
|
before(:each) do
|
118
118
|
do_post "/application/clientlogin", "login" => @u.login, "password" => 'testpass'
|
119
|
-
@source_config = {
|
120
|
-
"
|
119
|
+
@source_config = {
|
120
|
+
"sources"=> {
|
121
|
+
"FixedSchemaAdapter"=>
|
122
|
+
{"schema"=>{"property"=>[{"name"=>"string"},
|
123
|
+
{"brand"=>"string"}, {"price"=>"string"},
|
124
|
+
{"image_url"=>"blob"}], "version"=>"1.0",
|
125
|
+
"unique_index"=>[{"by_price"=>"price"}],
|
126
|
+
"index"=>[{"by_name_brand"=>"name,brand"}]},
|
127
|
+
"poll_interval"=>300},
|
128
|
+
"SampleAdapter"=>{"poll_interval"=>300},
|
129
|
+
"SimpleAdapter"=>{"partition_type"=>"app",
|
130
|
+
"poll_interval"=>600}}
|
131
|
+
}
|
121
132
|
end
|
122
133
|
|
123
134
|
it "should respond to clientcreate" do
|
@@ -190,6 +201,20 @@ describe "Server" do
|
|
190
201
|
verify_result("test_delete_storage" => {'1'=>@product1})
|
191
202
|
end
|
192
203
|
|
204
|
+
it "should handle client posting broken json" do
|
205
|
+
broken_json = "{\"foo\":\"bar\"\"}"
|
206
|
+
post "/application", broken_json, {'CONTENT_TYPE'=>'application/json'}
|
207
|
+
last_response.status.should == 500
|
208
|
+
last_response.body.should == "Server error while processing client data"
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should handle client posting broken body" do
|
212
|
+
broken_json = ['foo']
|
213
|
+
post "/application", broken_json, {'CONTENT_TYPE'=>'application/json'}
|
214
|
+
last_response.status.should == 500
|
215
|
+
last_response.body.should == "Internal server error"
|
216
|
+
end
|
217
|
+
|
193
218
|
it "should get inserts json" do
|
194
219
|
cs = ClientSync.new(@s,@c,1)
|
195
220
|
data = {'1'=>@product1,'2'=>@product2}
|
@@ -307,7 +332,7 @@ describe "Server" do
|
|
307
332
|
data = BulkData.load(bulk_data_docname(@a.id,@u.id))
|
308
333
|
last_response.body.should == {:result => :url,
|
309
334
|
:url => data.url}.to_json
|
310
|
-
|
335
|
+
validate_db(data,{@s.name => @data, 'FixedSchemaAdapter' => @data})
|
311
336
|
end
|
312
337
|
|
313
338
|
it "should download bulk data file" do
|
@@ -318,7 +343,7 @@ describe "Server" do
|
|
318
343
|
get JSON.parse(last_response.body)["url"]
|
319
344
|
last_response.should be_ok
|
320
345
|
File.open('test.data','wb') {|f| f.puts last_response.body}
|
321
|
-
|
346
|
+
validate_db_file('test.data',[@s.name,'FixedSchemaAdapter'],{@s.name => @data, 'FixedSchemaAdapter' => @data})
|
322
347
|
File.delete('test.data')
|
323
348
|
end
|
324
349
|
|
data/spec/spec_helper.rb
CHANGED
@@ -58,6 +58,10 @@ module TestHelpers
|
|
58
58
|
def delete_data_directory
|
59
59
|
FileUtils.rm_rf(Rhosync.data_directory)
|
60
60
|
end
|
61
|
+
|
62
|
+
def json_clone(data)
|
63
|
+
JSON.parse(data.to_json)
|
64
|
+
end
|
61
65
|
|
62
66
|
def set_state(state)
|
63
67
|
state.each do |dockey,data|
|
@@ -97,26 +101,52 @@ module TestHelpers
|
|
97
101
|
end
|
98
102
|
|
99
103
|
def validate_db(bulk_data,data)
|
100
|
-
|
104
|
+
validate_db_file(bulk_data.dbfile,bulk_data.sources.members,data)
|
105
|
+
end
|
106
|
+
|
107
|
+
def validate_db_file(dbfile,sources,data)
|
108
|
+
db = SQLite3::Database.new(dbfile)
|
109
|
+
sources.each do |source_name|
|
110
|
+
s = Source.load(source_name,{:app_id => APP_NAME,:user_id => @u.login})
|
111
|
+
return false unless validate_db_by_name(db,s,data[s.name])
|
112
|
+
end
|
113
|
+
true
|
101
114
|
end
|
102
115
|
|
103
|
-
def validate_db_by_name(
|
104
|
-
db
|
105
|
-
|
106
|
-
return false if row[0] !=
|
107
|
-
return false if row[1] !=
|
108
|
-
return false if row[2] !=
|
109
|
-
return false if row[3] !=
|
110
|
-
return false if row[4] !=
|
111
|
-
return false if row[5] != get_attrib_counter(data)
|
112
|
-
return false if row[6] !=
|
116
|
+
def validate_db_by_name(db,s,data)
|
117
|
+
db.execute("select source_id,name,sync_priority,partition,
|
118
|
+
sync_type,source_attribs,metadata from sources where name='#{s.name}'").each do |row|
|
119
|
+
return false if row[0] != s.source_id.to_s
|
120
|
+
return false if row[1] != s.name
|
121
|
+
return false if row[2] != s.priority.to_s
|
122
|
+
return false if row[3] != s.partition_type.to_s
|
123
|
+
return false if row[4] != s.sync_type.to_s
|
124
|
+
return false if row[5] != (s.schema ? "" : get_attrib_counter(data))
|
125
|
+
return false if row[6] != s.get_value(:metadata)
|
126
|
+
end
|
127
|
+
data = json_clone(data)
|
128
|
+
if s.schema
|
129
|
+
schema = JSON.parse(s.schema)
|
130
|
+
columns = ['object']
|
131
|
+
schema['property'].each do |column|
|
132
|
+
columns << column.keys[0]
|
133
|
+
end
|
134
|
+
db.execute("select #{columns.join(',')} from #{s.name}") do |row|
|
135
|
+
obj = data[row[0]]
|
136
|
+
columns.each_index do |i|
|
137
|
+
next if i == 0
|
138
|
+
return false if row[i] != obj[columns[i]]
|
139
|
+
end
|
140
|
+
data.delete(row[0])
|
141
|
+
end
|
142
|
+
else
|
143
|
+
db.execute("select * from object_values where source_id=#{s.source_id}").each do |row|
|
144
|
+
object = data[row[2]]
|
145
|
+
return false if object.nil? or object[row[1]] != row[3] or row[0] != s.source_id.to_s
|
146
|
+
object.delete(row[1])
|
147
|
+
data.delete(row[2]) if object.empty?
|
148
|
+
end
|
113
149
|
end
|
114
|
-
db.execute("select * from object_values").each do |row|
|
115
|
-
object = data[row[2]]
|
116
|
-
return false if object.nil? or object[row[1]] != row[3] or row[0] != @s.source_id.to_s
|
117
|
-
object.delete(row[1])
|
118
|
-
data.delete(row[2]) if object.empty?
|
119
|
-
end
|
120
150
|
data.empty?
|
121
151
|
end
|
122
152
|
|
@@ -232,6 +262,10 @@ describe "DBObjectsHelper", :shared => true do
|
|
232
262
|
@c = Client.create(@c_fields,{:source_name => @s_fields[:name]})
|
233
263
|
@s = Source.load(@s_fields[:name],@s_params)
|
234
264
|
@s = Source.create(@s_fields,@s_params) if @s.nil?
|
265
|
+
@s1 = Source.load('FixedSchemaAdapter',@s_params)
|
266
|
+
@s1 = Source.create({:name => 'FixedSchemaAdapter'},@s_params) if @s1.nil?
|
267
|
+
config = Rhosync.source_config["sources"]['FixedSchemaAdapter']
|
268
|
+
@s1.update(config)
|
235
269
|
@r = @s.read_state
|
236
270
|
@a.sources << @s.id
|
237
271
|
@a.users << @u.id
|
metadata
CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
|
|
6
6
|
- 2
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 2.0.0.
|
9
|
+
- beta4
|
10
|
+
version: 2.0.0.beta4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Rhomobile
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-05-
|
18
|
+
date: 2010-05-20 00:00:00 -07:00
|
19
19
|
default_executable: rhosync
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -412,6 +412,7 @@ files:
|
|
412
412
|
- spec/apps/rhotestapp/settings/license.key
|
413
413
|
- spec/apps/rhotestapp/settings/settings.yml
|
414
414
|
- spec/apps/rhotestapp/sources/base_adapter.rb
|
415
|
+
- spec/apps/rhotestapp/sources/fixed_schema_adapter.rb
|
415
416
|
- spec/apps/rhotestapp/sources/sample_adapter.rb
|
416
417
|
- spec/apps/rhotestapp/sources/simple_adapter.rb
|
417
418
|
- spec/apps/rhotestapp/sources/sub_adapter.rb
|
@@ -513,6 +514,7 @@ test_files:
|
|
513
514
|
- spec/app_spec.rb
|
514
515
|
- spec/apps/rhotestapp/application.rb
|
515
516
|
- spec/apps/rhotestapp/sources/base_adapter.rb
|
517
|
+
- spec/apps/rhotestapp/sources/fixed_schema_adapter.rb
|
516
518
|
- spec/apps/rhotestapp/sources/sample_adapter.rb
|
517
519
|
- spec/apps/rhotestapp/sources/simple_adapter.rb
|
518
520
|
- spec/apps/rhotestapp/sources/sub_adapter.rb
|