rhosync 2.0.0.beta3 → 2.0.0.beta4
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/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
|