rhosync 2.0.0.beta1
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 +5 -0
- data/LICENSE +674 -0
- data/README.md +26 -0
- data/Rakefile +109 -0
- data/bench/bench +6 -0
- data/bench/benchapp/Rakefile +14 -0
- data/bench/benchapp/application.rb +13 -0
- data/bench/benchapp/config.ru +32 -0
- data/bench/benchapp/settings/license.key +1 -0
- data/bench/benchapp/settings/settings.yml +18 -0
- data/bench/benchapp/sources/mock_adapter.rb +55 -0
- data/bench/benchapp/sources/queue_mock_adapter.rb +2 -0
- data/bench/benchapp/vendor/rhosync/lib/rhosync.rb +7 -0
- data/bench/lib/bench/cli.rb +16 -0
- data/bench/lib/bench/logging.rb +18 -0
- data/bench/lib/bench/mock_client.rb +41 -0
- data/bench/lib/bench/result.rb +50 -0
- data/bench/lib/bench/runner.rb +44 -0
- data/bench/lib/bench/session.rb +65 -0
- data/bench/lib/bench/statistics.rb +56 -0
- data/bench/lib/bench/test_data.rb +55 -0
- data/bench/lib/bench/timer.rb +10 -0
- data/bench/lib/bench/utils.rb +49 -0
- data/bench/lib/bench.rb +128 -0
- data/bench/lib/testdata/100-data.txt +148 -0
- data/bench/lib/testdata/5-data.txt +11 -0
- data/bench/scripts/cud_script.rb +77 -0
- data/bench/scripts/helpers.rb +101 -0
- data/bench/scripts/query_md_script.rb +46 -0
- data/bench/scripts/query_script.rb +46 -0
- data/bench/spec/bench_spec_helper.rb +65 -0
- data/bench/spec/logging_spec.rb +19 -0
- data/bench/spec/mock_adapter_spec.rb +61 -0
- data/bench/spec/mock_client_spec.rb +64 -0
- data/bench/spec/result_spec.rb +59 -0
- data/bench/spec/utils_spec.rb +35 -0
- data/bin/rhosync +34 -0
- data/doc/protocol.html +1901 -0
- data/doc/public/css/print.css +29 -0
- data/doc/public/css/screen.css +257 -0
- data/doc/public/css/style.css +20 -0
- data/examples/simple/application.rb +13 -0
- data/examples/simple/sources/sample_adapter.rb +5 -0
- data/examples/simple/sources/simple_adapter.rb +5 -0
- data/examples/simple/vendor/rhosync/lib/rhosync.rb +7 -0
- data/generators/rhosync.rb +98 -0
- data/generators/templates/application/Rakefile +19 -0
- data/generators/templates/application/application.rb +27 -0
- data/generators/templates/application/config.ru +33 -0
- data/generators/templates/application/settings/license.key +1 -0
- data/generators/templates/application/settings/settings.yml +14 -0
- data/generators/templates/source/source_adapter.rb +49 -0
- data/lib/rhosync/api/create_client.rb +3 -0
- data/lib/rhosync/api/create_user.rb +7 -0
- data/lib/rhosync/api/delete_client.rb +5 -0
- data/lib/rhosync/api/delete_user.rb +5 -0
- data/lib/rhosync/api/get_api_token.rb +7 -0
- data/lib/rhosync/api/get_client_params.rb +3 -0
- data/lib/rhosync/api/get_db_doc.rb +7 -0
- data/lib/rhosync/api/get_license_info.rb +7 -0
- data/lib/rhosync/api/get_source_params.rb +3 -0
- data/lib/rhosync/api/list_client_docs.rb +12 -0
- data/lib/rhosync/api/list_clients.rb +3 -0
- data/lib/rhosync/api/list_source_docs.rb +10 -0
- data/lib/rhosync/api/list_sources.rb +15 -0
- data/lib/rhosync/api/list_users.rb +3 -0
- data/lib/rhosync/api/ping.rb +7 -0
- data/lib/rhosync/api/push_deletes.rb +6 -0
- data/lib/rhosync/api/push_objects.rb +6 -0
- data/lib/rhosync/api/reset.rb +10 -0
- data/lib/rhosync/api/set_db_doc.rb +8 -0
- data/lib/rhosync/api/set_refresh_time.rb +8 -0
- data/lib/rhosync/api/update_user.rb +4 -0
- data/lib/rhosync/api/upload_file.rb +4 -0
- data/lib/rhosync/api_token.rb +19 -0
- data/lib/rhosync/app.rb +69 -0
- data/lib/rhosync/bulk_data/bulk_data.rb +75 -0
- data/lib/rhosync/bulk_data/syncdb.index.schema +3 -0
- data/lib/rhosync/bulk_data/syncdb.schema +37 -0
- data/lib/rhosync/bulk_data.rb +2 -0
- data/lib/rhosync/client.rb +74 -0
- data/lib/rhosync/client_sync.rb +296 -0
- data/lib/rhosync/console/app/helpers/auth_helper.rb +18 -0
- data/lib/rhosync/console/app/helpers/extensions.rb +19 -0
- data/lib/rhosync/console/app/helpers/helpers.rb +52 -0
- data/lib/rhosync/console/app/public/main.css +7 -0
- data/lib/rhosync/console/app/public/text.txt +0 -0
- data/lib/rhosync/console/app/routes/auth.rb +29 -0
- data/lib/rhosync/console/app/routes/client.rb +32 -0
- data/lib/rhosync/console/app/routes/docs.rb +84 -0
- data/lib/rhosync/console/app/routes/home.rb +22 -0
- data/lib/rhosync/console/app/routes/user.rb +63 -0
- data/lib/rhosync/console/app/views/client.erb +30 -0
- data/lib/rhosync/console/app/views/doc.erb +56 -0
- data/lib/rhosync/console/app/views/docs.erb +29 -0
- data/lib/rhosync/console/app/views/index.erb +50 -0
- data/lib/rhosync/console/app/views/layout.erb +12 -0
- data/lib/rhosync/console/app/views/newuser.erb +17 -0
- data/lib/rhosync/console/app/views/ping.erb +28 -0
- data/lib/rhosync/console/app/views/result.erb +11 -0
- data/lib/rhosync/console/app/views/user.erb +32 -0
- data/lib/rhosync/console/app/views/users.erb +14 -0
- data/lib/rhosync/console/rhosync_api.rb +102 -0
- data/lib/rhosync/console/server.rb +27 -0
- data/lib/rhosync/credential.rb +9 -0
- data/lib/rhosync/document.rb +43 -0
- data/lib/rhosync/indifferent_access.rb +132 -0
- data/lib/rhosync/jobs/bulk_data_job.rb +104 -0
- data/lib/rhosync/jobs/ping_job.rb +19 -0
- data/lib/rhosync/jobs/source_job.rb +16 -0
- data/lib/rhosync/license.rb +79 -0
- data/lib/rhosync/lock_ops.rb +11 -0
- data/lib/rhosync/model.rb +410 -0
- data/lib/rhosync/ping/blackberry.rb +55 -0
- data/lib/rhosync/ping/iphone.rb +44 -0
- data/lib/rhosync/ping.rb +2 -0
- data/lib/rhosync/read_state.rb +27 -0
- data/lib/rhosync/server/views/index.erb +12 -0
- data/lib/rhosync/server.rb +242 -0
- data/lib/rhosync/source.rb +112 -0
- data/lib/rhosync/source_adapter.rb +95 -0
- data/lib/rhosync/source_sync.rb +245 -0
- data/lib/rhosync/store.rb +199 -0
- data/lib/rhosync/tasks.rb +151 -0
- data/lib/rhosync/user.rb +83 -0
- data/lib/rhosync/version.rb +3 -0
- data/lib/rhosync.rb +251 -0
- data/spec/api/api_helper.rb +44 -0
- data/spec/api/create_client_spec.rb +13 -0
- data/spec/api/create_user_spec.rb +16 -0
- data/spec/api/delete_client_spec.rb +13 -0
- data/spec/api/delete_user_spec.rb +18 -0
- data/spec/api/get_api_token_spec.rb +25 -0
- data/spec/api/get_client_params_spec.rb +18 -0
- data/spec/api/get_db_doc_spec.rb +21 -0
- data/spec/api/get_license_info_spec.rb +16 -0
- data/spec/api/get_source_params_spec.rb +26 -0
- data/spec/api/list_client_docs_spec.rb +33 -0
- data/spec/api/list_clients_spec.rb +23 -0
- data/spec/api/list_source_docs_spec.rb +26 -0
- data/spec/api/list_sources_spec.rb +27 -0
- data/spec/api/list_users_spec.rb +21 -0
- data/spec/api/ping_spec.rb +24 -0
- data/spec/api/push_deletes_spec.rb +16 -0
- data/spec/api/push_objects_spec.rb +27 -0
- data/spec/api/reset_spec.rb +22 -0
- data/spec/api/set_db_doc_spec.rb +20 -0
- data/spec/api/set_refresh_time_spec.rb +43 -0
- data/spec/api/update_user_spec.rb +31 -0
- data/spec/api/upload_file_spec.rb +26 -0
- data/spec/api_token_spec.rb +13 -0
- data/spec/app_spec.rb +20 -0
- data/spec/apps/rhotestapp/Rakefile +1 -0
- data/spec/apps/rhotestapp/application.rb +16 -0
- data/spec/apps/rhotestapp/config.ru +1 -0
- data/spec/apps/rhotestapp/settings/apple_fake_cert.pem +1 -0
- data/spec/apps/rhotestapp/settings/license.key +1 -0
- data/spec/apps/rhotestapp/settings/settings.yml +23 -0
- data/spec/apps/rhotestapp/sources/base_adapter.rb +9 -0
- data/spec/apps/rhotestapp/sources/sample_adapter.rb +66 -0
- data/spec/apps/rhotestapp/sources/simple_adapter.rb +39 -0
- data/spec/apps/rhotestapp/sources/sub_adapter.rb +7 -0
- data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem/mygem.rb +8 -0
- data/spec/apps/rhotestapp/vendor/mygem-0.1.0/lib/mygem.rb +1 -0
- data/spec/bulk_data/bulk_data_spec.rb +79 -0
- data/spec/client_spec.rb +58 -0
- data/spec/client_sync_spec.rb +377 -0
- data/spec/doc/base.html +72 -0
- data/spec/doc/doc_spec.rb +303 -0
- data/spec/doc/footer.html +4 -0
- data/spec/doc/header.html +30 -0
- data/spec/document_spec.rb +27 -0
- data/spec/generator/generator_spec.rb +53 -0
- data/spec/generator/generator_spec_helper.rb +8 -0
- data/spec/jobs/bulk_data_job_spec.rb +76 -0
- data/spec/jobs/ping_job_spec.rb +26 -0
- data/spec/jobs/source_job_spec.rb +25 -0
- data/spec/license_spec.rb +48 -0
- data/spec/model_spec.rb +269 -0
- data/spec/perf/bulk_data_perf_spec.rb +33 -0
- data/spec/perf/perf_spec_helper.rb +51 -0
- data/spec/perf/store_perf_spec.rb +28 -0
- data/spec/ping/blackberry_spec.rb +62 -0
- data/spec/ping/iphone_spec.rb +50 -0
- data/spec/read_state_spec.rb +25 -0
- data/spec/rhosync_spec.rb +43 -0
- data/spec/server/server_spec.rb +341 -0
- data/spec/source_adapter_spec.rb +114 -0
- data/spec/source_spec.rb +77 -0
- data/spec/source_sync_spec.rb +248 -0
- data/spec/spec_helper.rb +240 -0
- data/spec/store_spec.rb +149 -0
- data/spec/sync_states_spec.rb +101 -0
- data/spec/testdata/1000-data.txt +1414 -0
- data/spec/testdata/compressed/compress-data.txt +1 -0
- data/spec/testdata/upload1.txt +1 -0
- data/spec/testdata/upload2.txt +1 -0
- data/spec/user_spec.rb +79 -0
- data/tasks/redis.rake +134 -0
- metadata +545 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Rhosync::Server.api :list_source_docs do |params,user|
|
|
2
|
+
res = {}
|
|
3
|
+
s = Source.load(params[:source_id], {:app_id => APP_NAME,:user_id => params[:user_id]})
|
|
4
|
+
[:md,:md_size,:md_copy,:errors].each do |doc|
|
|
5
|
+
db_key = s.docname(doc)
|
|
6
|
+
res.merge!(doc => db_key)
|
|
7
|
+
end
|
|
8
|
+
res.to_json
|
|
9
|
+
end
|
|
10
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Rhosync::Server.api :list_sources do |params,user|
|
|
2
|
+
sources = App.load(APP_NAME).sources.members
|
|
3
|
+
if params[:partition_type].nil? or params[:partition_type] == 'all'
|
|
4
|
+
sources.to_json
|
|
5
|
+
else
|
|
6
|
+
res = []
|
|
7
|
+
sources.each do |name|
|
|
8
|
+
s = Source.load(name,{:app_id => APP_NAME,:user_id => '*'})
|
|
9
|
+
if s.partition_type and s.partition_type == params[:partition_type]
|
|
10
|
+
res << name
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
res.to_json
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Rhosync::Server.api :reset do |params,user|
|
|
2
|
+
Store.db.flushdb
|
|
3
|
+
app_klass = Object.const_get(camelize(APP_NAME))
|
|
4
|
+
if app_klass.singleton_methods.include?("initializer")
|
|
5
|
+
app_klass.send :initializer, Rhosync.base_directory
|
|
6
|
+
end
|
|
7
|
+
# restoring previous token value after flushdb
|
|
8
|
+
user.token = params[:api_token]
|
|
9
|
+
"DB reset"
|
|
10
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Rhosync::Server.api :set_refresh_time do |params,user|
|
|
2
|
+
source = Source.load(params[:source_name],
|
|
3
|
+
{:app_id => APP_NAME, :user_id => params[:user_name]})
|
|
4
|
+
source.poll_interval = params[:poll_interval] if params[:poll_interval]
|
|
5
|
+
params[:refresh_time] ||= 0
|
|
6
|
+
source.read_state.refresh_time = Time.now.to_i + params[:refresh_time].to_i
|
|
7
|
+
''
|
|
8
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'uuidtools'
|
|
2
|
+
module Rhosync
|
|
3
|
+
class ApiToken < Model
|
|
4
|
+
field :value,:string
|
|
5
|
+
field :user_id,:string
|
|
6
|
+
validates_presence_of :user_id
|
|
7
|
+
|
|
8
|
+
def self.create(fields)
|
|
9
|
+
fields[:value] = fields[:value] || get_random_uuid
|
|
10
|
+
fields[:id] = fields[:value]
|
|
11
|
+
object = super(fields)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def user
|
|
15
|
+
@user ||= User.load(self.user_id)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
data/lib/rhosync/app.rb
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Rhosync
|
|
2
|
+
class App < Model
|
|
3
|
+
field :name, :string
|
|
4
|
+
set :users, :string
|
|
5
|
+
set :sources, :string
|
|
6
|
+
attr_reader :delegate
|
|
7
|
+
validates_presence_of :name
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def create(fields={})
|
|
11
|
+
fields[:id] = fields[:name]
|
|
12
|
+
begin
|
|
13
|
+
require under_score(fields[:name])
|
|
14
|
+
rescue Exception; end
|
|
15
|
+
super(fields)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def can_authenticate?
|
|
20
|
+
self.delegate && self.delegate.singleton_methods.include?("authenticate")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def authenticate(login, password, session)
|
|
24
|
+
if self.delegate && self.delegate.authenticate(login, password, session)
|
|
25
|
+
user = User.load(login) if User.is_exist?(login)
|
|
26
|
+
if not user
|
|
27
|
+
user = User.create(:login => login)
|
|
28
|
+
users << user.id
|
|
29
|
+
end
|
|
30
|
+
return user
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# TODO: Do we need this anymore?
|
|
35
|
+
# def delete
|
|
36
|
+
# sources.members.each do |source_name|
|
|
37
|
+
# Source.load(source_name,{:app_id => self.name,
|
|
38
|
+
# :user_id => '*'}).delete
|
|
39
|
+
# end
|
|
40
|
+
# users.members.each do |user_name|
|
|
41
|
+
# User.load(user_name).delete
|
|
42
|
+
# end
|
|
43
|
+
# ReadState.delete(self.name)
|
|
44
|
+
# super
|
|
45
|
+
# end
|
|
46
|
+
|
|
47
|
+
def delegate
|
|
48
|
+
@delegate.nil? ? Object.const_get(camelize(self.name)) : @delegate
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def partition_sources(partition,user_id)
|
|
52
|
+
names = []
|
|
53
|
+
need_refresh = false
|
|
54
|
+
sources.members.each do |source|
|
|
55
|
+
s = Source.load(source,{:app_id => self.name,
|
|
56
|
+
:user_id => user_id})
|
|
57
|
+
if s.partition == partition
|
|
58
|
+
names << s.name
|
|
59
|
+
need_refresh = true if !need_refresh and s.check_refresh_time
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
{:names => names,:need_refresh => need_refresh}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def store_blob(obj,field_name,blob)
|
|
66
|
+
self.delegate.send :store_blob, obj,field_name,blob
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require 'resque'
|
|
2
|
+
require 'rhosync/jobs/bulk_data_job'
|
|
3
|
+
|
|
4
|
+
module Rhosync
|
|
5
|
+
class BulkData < Model
|
|
6
|
+
field :name, :string
|
|
7
|
+
field :state, :string
|
|
8
|
+
field :app_id, :string
|
|
9
|
+
field :user_id, :string
|
|
10
|
+
field :refresh_time, :integer
|
|
11
|
+
field :dbfile,:string
|
|
12
|
+
set :sources, :string
|
|
13
|
+
validates_presence_of :app_id, :user_id, :sources
|
|
14
|
+
|
|
15
|
+
def completed?
|
|
16
|
+
if state.to_sym == :completed and
|
|
17
|
+
dbfile and File.exist?(dbfile)
|
|
18
|
+
return true
|
|
19
|
+
end
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def delete
|
|
24
|
+
sources.members.each do |source|
|
|
25
|
+
s = Source.load(source,{:app_id => app_id, :user_id => user_id})
|
|
26
|
+
Store.flash_data(s.docname(:md_copy)) if s
|
|
27
|
+
end
|
|
28
|
+
super
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def process_sources
|
|
32
|
+
sources.members.each do |source|
|
|
33
|
+
s = Source.load(source,{:app_id => app_id, :user_id => user_id})
|
|
34
|
+
if s
|
|
35
|
+
SourceSync.new(s).process_query(nil)
|
|
36
|
+
s.clone(:md,:md_copy) unless s.sync_type.to_sym == :bulk_sync_only
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def url
|
|
42
|
+
dbfile
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
def create(fields={})
|
|
47
|
+
fields[:id] = fields[:name]
|
|
48
|
+
fields[:state] ||= :inprogress
|
|
49
|
+
fields[:sources] ||= []
|
|
50
|
+
super(fields)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def enqueue(params={})
|
|
54
|
+
Resque.enqueue(BulkDataJob,params)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def get_name(partition,client)
|
|
58
|
+
if partition == :user
|
|
59
|
+
File.join(client.app_id,client.user_id,client.user_id)
|
|
60
|
+
else
|
|
61
|
+
File.join(client.app_id,client.app_id)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def schema_file
|
|
66
|
+
File.join(File.dirname(__FILE__),'syncdb.schema')
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def index_file
|
|
70
|
+
File.join(File.dirname(__FILE__),'syncdb.index.schema')
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
CREATE TABLE client_info (
|
|
2
|
+
client_id VARCHAR(255) default NULL,
|
|
3
|
+
session VARCHAR(255) default NULL,
|
|
4
|
+
token VARCHAR(255) default NULL,
|
|
5
|
+
token_sent BIGINT default 0,
|
|
6
|
+
reset BIGINT default 0,
|
|
7
|
+
port VARCHAR(10) default NULL,
|
|
8
|
+
bulksync_state BIGINT default 0,
|
|
9
|
+
last_sync_success VARCHAR(100) default NULL);
|
|
10
|
+
CREATE TABLE object_values (
|
|
11
|
+
source_id BIGINT default NULL,
|
|
12
|
+
attrib varchar(255) default NULL,
|
|
13
|
+
object varchar(255) default NULL,
|
|
14
|
+
value varchar default NULL,
|
|
15
|
+
attrib_type varchar(255) default NULL);
|
|
16
|
+
CREATE TABLE changed_values (
|
|
17
|
+
source_id BIGINT default NULL,
|
|
18
|
+
attrib varchar(255) default NULL,
|
|
19
|
+
object varchar(255) default NULL,
|
|
20
|
+
value varchar default NULL,
|
|
21
|
+
attrib_type varchar(255) default NULL,
|
|
22
|
+
update_type varchar(255) default NULL,
|
|
23
|
+
sent BIGINT default 0);
|
|
24
|
+
CREATE TABLE sources (
|
|
25
|
+
source_id BIGINT PRIMARY KEY,
|
|
26
|
+
name VARCHAR(255) default NULL,
|
|
27
|
+
token BIGINT default NULL,
|
|
28
|
+
priority BIGINT,
|
|
29
|
+
partition VARCHAR(255),
|
|
30
|
+
sync_type VARCHAR(255),
|
|
31
|
+
last_updated BIGINT default 0,
|
|
32
|
+
last_inserted_size BIGINT default 0,
|
|
33
|
+
last_deleted_size BIGINT default 0,
|
|
34
|
+
last_sync_duration BIGINT default 0,
|
|
35
|
+
last_sync_success BIGINT default 0,
|
|
36
|
+
backend_refresh_time BIGINT default 0,
|
|
37
|
+
source_attribs varchar default NULL);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Rhosync
|
|
2
|
+
class InvalidSourceNameError < RuntimeError; end
|
|
3
|
+
|
|
4
|
+
class Client < Model
|
|
5
|
+
field :device_type,:string
|
|
6
|
+
field :device_pin,:string
|
|
7
|
+
field :device_port,:string
|
|
8
|
+
|
|
9
|
+
field :user_id,:string
|
|
10
|
+
field :app_id,:string
|
|
11
|
+
attr_accessor :source_name
|
|
12
|
+
validates_presence_of :app_id, :user_id
|
|
13
|
+
|
|
14
|
+
include Document
|
|
15
|
+
include LockOps
|
|
16
|
+
|
|
17
|
+
def self.create(fields,params={})
|
|
18
|
+
Rhosync.license.check_and_use_seat
|
|
19
|
+
fields[:id] = get_random_uuid
|
|
20
|
+
res = super(fields,params)
|
|
21
|
+
user = User.load(fields[:user_id])
|
|
22
|
+
user.clients << res.id
|
|
23
|
+
res
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.load(id,params)
|
|
27
|
+
validate_attributes(params)
|
|
28
|
+
super(id,params)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def app
|
|
32
|
+
@app ||= App.load(app_id)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def doc_suffix(doctype)
|
|
36
|
+
doctype = doctype.to_s
|
|
37
|
+
if doctype == '*'
|
|
38
|
+
"#{self.user_id}:#{self.id}:*"
|
|
39
|
+
elsif self.source_name
|
|
40
|
+
"#{self.user_id}:#{self.id}:#{self.source_name}:#{doctype}"
|
|
41
|
+
else
|
|
42
|
+
raise InvalidSourceNameError.new('Invalid Source Name For Client')
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def delete
|
|
47
|
+
flash_data('*')
|
|
48
|
+
Rhosync.license.free_seat
|
|
49
|
+
super
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def update_clientdoc(sources)
|
|
53
|
+
sources.each do |source|
|
|
54
|
+
s = Source.load(source,{:app_id => app_id,:user_id => user_id})
|
|
55
|
+
unless s.sync_type.to_sym == :bulk_sync_only
|
|
56
|
+
self.source_name = source
|
|
57
|
+
Store.clone(s.docname(:md_copy),self.docname(:cd))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def update_fields(params)
|
|
63
|
+
[:device_type,:device_pin,:device_port].each do |setting|
|
|
64
|
+
self.send "#{setting}=".to_sym, params[setting].to_s if params[setting]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def self.validate_attributes(params)
|
|
71
|
+
raise ArgumentError.new('Missing required attribute source_name') unless params[:source_name]
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
module Rhosync
|
|
2
|
+
class ClientSync
|
|
3
|
+
attr_accessor :source,:client,:p_size,:source_sync
|
|
4
|
+
|
|
5
|
+
VERSION = 3
|
|
6
|
+
|
|
7
|
+
def initialize(source,client,p_size=nil)
|
|
8
|
+
raise ArgumentError.new('Missing required attribute client') unless client
|
|
9
|
+
raise ArgumentError.new('Missing required attribute source') unless source
|
|
10
|
+
@source,@client,@p_size = source,client,p_size ? p_size.to_i : 500
|
|
11
|
+
@source_sync = SourceSync.new(@source)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def receive_cud(cud_params={},query_params=nil)
|
|
15
|
+
_process_blobs(cud_params)
|
|
16
|
+
processed = 0
|
|
17
|
+
['create','update','delete'].each do |op|
|
|
18
|
+
key,value = op,cud_params[op]
|
|
19
|
+
processed += _receive_cud(key,value) if value
|
|
20
|
+
end
|
|
21
|
+
@source_sync.process_cud(@client.id) if processed > 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def send_cud(token=nil,query_params=nil)
|
|
25
|
+
res = []
|
|
26
|
+
if not _ack_token(token)
|
|
27
|
+
res = resend_page(token)
|
|
28
|
+
else
|
|
29
|
+
@source_sync.process_query(query_params)
|
|
30
|
+
res = send_new_page
|
|
31
|
+
end
|
|
32
|
+
_format_result(res[0],res[1],res[2],res[3])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def search(params)
|
|
36
|
+
res = []
|
|
37
|
+
return _resend_search_result(params[:search_token]) if params[:search_token] and params[:resend]
|
|
38
|
+
_ack_search(params[:search_token]) if params[:search_token]
|
|
39
|
+
res = _do_search(params[:search]) if params[:search]
|
|
40
|
+
res
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def build_page
|
|
44
|
+
res = {}
|
|
45
|
+
yield res
|
|
46
|
+
res.reject! {|key,value| value.nil? or value.empty?}
|
|
47
|
+
res.merge!(_send_errors)
|
|
48
|
+
res
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def send_new_page
|
|
52
|
+
compute_errors_page
|
|
53
|
+
token,progress_count,total_count = '',0,0
|
|
54
|
+
res = build_page do |r|
|
|
55
|
+
progress_count,total_count,r['insert'] = compute_page
|
|
56
|
+
r['delete'] = compute_deleted_page
|
|
57
|
+
r['links'] = compute_links_page
|
|
58
|
+
r['metadata'] = compute_metadata
|
|
59
|
+
end
|
|
60
|
+
if res['insert'] or res['delete'] or res['links']
|
|
61
|
+
token = compute_token(@client.docname(:page_token))
|
|
62
|
+
else
|
|
63
|
+
_delete_errors_page
|
|
64
|
+
end
|
|
65
|
+
@client.put_data(:cd,res['insert'],true)
|
|
66
|
+
@client.delete_data(:cd,res['delete'])
|
|
67
|
+
[token,progress_count,total_count,res]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Resend token for a client, also sends exceptions
|
|
71
|
+
def resend_page(token=nil)
|
|
72
|
+
token,progress_count,total_count = '',0,0
|
|
73
|
+
res = build_page do |r|
|
|
74
|
+
r['insert'] = @client.get_data(:page)
|
|
75
|
+
r['delete'] = @client.get_data(:delete_page)
|
|
76
|
+
r['links'] = @client.get_data(:create_links_page)
|
|
77
|
+
r['metadata'] = compute_metadata
|
|
78
|
+
progress_count = @client.get_value(:cd_size).to_i
|
|
79
|
+
total_count = @client.get_value(:total_count_page).to_i
|
|
80
|
+
end
|
|
81
|
+
token = @client.get_value(:page_token)
|
|
82
|
+
[token,progress_count,total_count,res]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Computes the metadata sha1 and returns metadata if client's sha1 doesn't
|
|
86
|
+
# match source's sha1
|
|
87
|
+
def compute_metadata
|
|
88
|
+
metadata_sha1,metadata = @source.lock(:metadata) do |s|
|
|
89
|
+
[s.get_value(:metadata_sha1),s.get_value(:metadata)]
|
|
90
|
+
end
|
|
91
|
+
return if @client.get_value(:metadata_sha1) == metadata_sha1
|
|
92
|
+
@client.put_value(:metadata_sha1,metadata_sha1)
|
|
93
|
+
metadata
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Computes diffs between master doc and client doc, trims it to page size,
|
|
97
|
+
# stores page, and returns page as hash
|
|
98
|
+
def compute_page
|
|
99
|
+
res,diffsize,total_count = @source.lock(:md) do |s|
|
|
100
|
+
res,diffsize = Store.get_diff_data(@client.docname(:cd),s.docname(:md),@p_size)
|
|
101
|
+
total_count = s.get_value(:md_size).to_i
|
|
102
|
+
[res,diffsize,total_count]
|
|
103
|
+
end
|
|
104
|
+
@client.put_data(:page,res)
|
|
105
|
+
progress_count = total_count - diffsize
|
|
106
|
+
@client.put_value(:cd_size,progress_count)
|
|
107
|
+
@client.put_value(:total_count_page,total_count)
|
|
108
|
+
[progress_count,total_count,res]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Computes search hash
|
|
112
|
+
def compute_search
|
|
113
|
+
Store.get_diff_data(@client.docname(:cd),@client.docname(:search),@p_size)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Computes deleted objects (down to individual attributes)
|
|
117
|
+
# in the client document, trims it to page size, stores page, and returns page as hash
|
|
118
|
+
def compute_deleted_page
|
|
119
|
+
res = {}
|
|
120
|
+
delete_page_doc = @client.docname(:delete_page)
|
|
121
|
+
page_size = @p_size
|
|
122
|
+
diff = @source.lock(:md) { |s| Store.get_diff_data(s.docname(:md),@client.docname(:cd))[0] }
|
|
123
|
+
diff.each do |key,value|
|
|
124
|
+
res[key] = value
|
|
125
|
+
value.each do |attrib,val|
|
|
126
|
+
Store.db.sadd(delete_page_doc,setelement(key,attrib,val))
|
|
127
|
+
end
|
|
128
|
+
page_size -= 1
|
|
129
|
+
break if page_size <= 0
|
|
130
|
+
end
|
|
131
|
+
res
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Computes errors for client and stores a copy as errors page
|
|
135
|
+
def compute_errors_page
|
|
136
|
+
['create','update','delete'].each do |operation|
|
|
137
|
+
@client.lock("#{operation}_errors") do |c|
|
|
138
|
+
c.rename("#{operation}_errors","#{operation}_errors_page")
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Computes create links for a client and stores a copy as links page
|
|
144
|
+
def compute_links_page
|
|
145
|
+
@client.lock(:create_links) do |c|
|
|
146
|
+
c.rename(:create_links,:create_links_page)
|
|
147
|
+
c.get_data(:create_links_page)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
class << self
|
|
152
|
+
# Resets the store for a given app,client
|
|
153
|
+
def reset(client)
|
|
154
|
+
client.flash_data('*') if client
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def search_all(client,params=nil)
|
|
158
|
+
return [] unless params[:sources]
|
|
159
|
+
res = []
|
|
160
|
+
params[:sources].each do |source|
|
|
161
|
+
s = Source.load(source,{:app_id => client.app_id,
|
|
162
|
+
:user_id => client.user_id})
|
|
163
|
+
client.source_name = source
|
|
164
|
+
cs = ClientSync.new(s,client,params[:p_size])
|
|
165
|
+
search_res = cs.search(params)
|
|
166
|
+
res << search_res if search_res
|
|
167
|
+
end
|
|
168
|
+
res
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def bulk_data(partition,client)
|
|
172
|
+
name = BulkData.get_name(partition,client)
|
|
173
|
+
data = BulkData.load(name)
|
|
174
|
+
sources = client.app.partition_sources(partition,client.user_id)
|
|
175
|
+
if (data.nil? or (data.completed? and sources[:need_refresh] == true)) and
|
|
176
|
+
sources[:names].length > 0
|
|
177
|
+
data.delete if data
|
|
178
|
+
data = BulkData.create(:name => name,
|
|
179
|
+
:app_id => client.app_id,
|
|
180
|
+
:user_id => client.user_id,
|
|
181
|
+
:sources => sources[:names])
|
|
182
|
+
BulkData.enqueue("data_name" => name)
|
|
183
|
+
end
|
|
184
|
+
if data and data.completed?
|
|
185
|
+
client.update_clientdoc(sources[:names])
|
|
186
|
+
{:result => :url, :url => data.url}
|
|
187
|
+
elsif data
|
|
188
|
+
{:result => :wait}
|
|
189
|
+
else
|
|
190
|
+
{:result => :nop}
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
private
|
|
196
|
+
def _resend_search_result(search_token)
|
|
197
|
+
_format_search_result
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def _ack_search(search_token)
|
|
201
|
+
token = @client.get_value(:search_token)
|
|
202
|
+
if token == search_token
|
|
203
|
+
@client.flash_data('search*')
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def _do_search(params)
|
|
208
|
+
@source_sync.search(@client.id,params)
|
|
209
|
+
_format_search_result
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def _format_search_result
|
|
213
|
+
error = @client.get_data(:search_errors)
|
|
214
|
+
if not error.empty?
|
|
215
|
+
[ {'version'=>VERSION},
|
|
216
|
+
{'source'=>@source.name},
|
|
217
|
+
{'search-error'=>error} ]
|
|
218
|
+
else
|
|
219
|
+
search_token = @client.get_value(:search_token)
|
|
220
|
+
search_token ||= ''
|
|
221
|
+
res,diffsize = compute_search
|
|
222
|
+
return [] if res.empty?
|
|
223
|
+
[ {'version'=>VERSION},
|
|
224
|
+
{'search_token' => search_token},
|
|
225
|
+
{'source'=>@source.name},
|
|
226
|
+
{'count'=>res.size},
|
|
227
|
+
{'insert'=>res} ]
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def _receive_cud(operation,params)
|
|
232
|
+
return 0 if not ['create','update','delete'].include?(operation)
|
|
233
|
+
@client.lock(operation) { |c| c.put_data(operation,params,true) }
|
|
234
|
+
return 1
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def _process_blobs(params)
|
|
238
|
+
unless params[:blob_fields].nil?
|
|
239
|
+
[:create,:update].each do |utype|
|
|
240
|
+
objects = params[utype] || {}
|
|
241
|
+
objects.each do |id,obj|
|
|
242
|
+
params[:blob_fields].each do |field|
|
|
243
|
+
blob = params["#{field}-#{id}"]
|
|
244
|
+
obj[field] = @client.app.store_blob(obj,field,blob)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def _ack_token(token)
|
|
252
|
+
stored_token = @client.get_value(:page_token)
|
|
253
|
+
if stored_token
|
|
254
|
+
if token and stored_token == token
|
|
255
|
+
@client.put_value(:page_token,nil)
|
|
256
|
+
@client.flash_data(:create_links_page)
|
|
257
|
+
@client.flash_data(:page)
|
|
258
|
+
@client.flash_data(:delete_page)
|
|
259
|
+
_delete_errors_page
|
|
260
|
+
return true
|
|
261
|
+
end
|
|
262
|
+
else
|
|
263
|
+
return true
|
|
264
|
+
end
|
|
265
|
+
false
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def _delete_errors_page
|
|
269
|
+
['create','update','delete'].each do |operation|
|
|
270
|
+
@client.flash_data("#{operation}_errors_page")
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def _send_errors
|
|
275
|
+
res = {}
|
|
276
|
+
['create','update','delete'].each do |operation|
|
|
277
|
+
res["#{operation}-error"] = @client.get_data("#{operation}_errors_page")
|
|
278
|
+
end
|
|
279
|
+
res["source-error"] = @source.lock(:errors) { |s| s.get_data(:errors) }
|
|
280
|
+
res.reject! {|key,value| value.nil? or value.empty?}
|
|
281
|
+
res
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def _format_result(token,progress_count,total_count,res)
|
|
285
|
+
count = 0
|
|
286
|
+
count += res['insert'].length if res['insert']
|
|
287
|
+
count += res['delete'].length if res['delete']
|
|
288
|
+
[ {'version'=>VERSION},
|
|
289
|
+
{'token'=>(token ? token : '')},
|
|
290
|
+
{'count'=>count},
|
|
291
|
+
{'progress_count'=>progress_count},
|
|
292
|
+
{'total_count'=>total_count},
|
|
293
|
+
res ]
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|