rhoconnect 4.0.0.beta.12 → 4.0.0.beta.24
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +34 -0
- data/Gemfile +5 -8
- data/Gemfile.lock +34 -32
- data/Rakefile +2 -12
- data/bench/run_bench.sh +1 -1
- data/bench/spec/bench_spec_helper.rb +3 -5
- data/bin/rhoconnect +20 -13
- data/commands/dtach/dtach_install.rb +3 -3
- data/commands/generators/update.rb +1 -0
- data/commands/parser.rb +180 -0
- data/commands/rhoconnect/config.rb +8 -4
- data/commands/rhoconnect/get_token.rb +3 -1
- data/commands/rhoconnect/routes.rb +34 -0
- data/commands/rhoconnect/secret.rb +1 -1
- data/commands/rhoconnect/set_admin_password.rb +2 -1
- data/commands/rhoconnect/startbg.rb +4 -4
- data/commands/rhoconnect/stop.rb +2 -2
- data/commands/rhoconnect_console/console.rb +6 -6
- data/commands/rhoconnect_spec/spec.rb +2 -1
- data/commands/rhoconnect_war/war.rb +1 -0
- data/commands/utilities/redis_runner.rb +2 -2
- data/doc/data-partitioning.txt +20 -7
- data/doc/extending-rhoconnect-server.txt +36 -7
- data/doc/push-client-setup-rps.txt +3 -3
- data/doc/source-adapters-js.txt +41 -4
- data/doc/source-adapters.txt +3 -0
- data/generators/templates/application/rcgemfile +1 -5
- data/generators/templates/application/spec/spec_helper.rb +0 -9
- data/generators/templates/source/models/js/model.js +8 -0
- data/generators/templates/source/models/ruby/model.rb +5 -11
- data/install.sh +2 -2
- data/installer/unix-like/rho_connect_install_constants.rb +3 -3
- data/installer/utils/delete_from_s3.rb +3 -6
- data/installer/utils/download_from_s3.rb +5 -9
- data/installer/utils/verify_checksum.rb +16 -11
- data/js-adapters/ballroom.js +9 -0
- data/js-adapters/exceptions.js +60 -0
- data/js-adapters/node.rb +14 -5
- data/js-adapters/node_channel.rb +68 -48
- data/js-adapters/rhoconnect_helpers.js +8 -2
- data/js-adapters/router.js +16 -14
- data/lib/rhoconnect.rb +5 -5
- data/lib/rhoconnect/client.rb +2 -2
- data/lib/rhoconnect/condition/add_parameter.rb +21 -0
- data/lib/rhoconnect/controller/clients_controller.rb +1 -1
- data/lib/rhoconnect/controller/js_base.rb +1 -2
- data/lib/rhoconnect/controller/system_controller.rb +33 -10
- data/lib/rhoconnect/db_adapter.rb +1 -1
- data/lib/rhoconnect/document.rb +11 -3
- data/lib/rhoconnect/handler/changes.rb +20 -19
- data/lib/rhoconnect/handler/changes/engine.rb +48 -25
- data/lib/rhoconnect/handler/changes/hooks.rb +36 -0
- data/lib/rhoconnect/handler/changes/runner.rb +12 -2
- data/lib/rhoconnect/handler/helpers.rb +4 -4
- data/lib/rhoconnect/jobs/source_job.rb +1 -1
- data/lib/rhoconnect/model/base.rb +32 -8
- data/lib/rhoconnect/model/helpers.rb +15 -0
- data/lib/rhoconnect/model/helpers/find_duplicates_on_update.rb +85 -0
- data/lib/rhoconnect/model/js_base.rb +23 -28
- data/lib/rhoconnect/server.rb +23 -18
- data/lib/rhoconnect/source.rb +10 -17
- data/lib/rhoconnect/store.rb +36 -57
- data/lib/rhoconnect/test_methods.rb +5 -4
- data/lib/rhoconnect/utilities.rb +7 -5
- data/lib/rhoconnect/version.rb +2 -2
- data/lib/rhoconnect/web-console/server.rb +17 -16
- data/rhoconnect.gemspec +23 -24
- data/spec/apps/rhotestapp/controllers/js/js_sample_controller.js +4 -0
- data/spec/apps/rhotestapp/models/js/js_sample.js +36 -0
- data/spec/apps/rhotestapp/models/ruby/sample_adapter.rb +34 -19
- data/spec/async_spec.rb +1 -1
- data/spec/cli/cli_spec.rb +69 -31
- data/spec/client_sync_spec.rb +30 -13
- data/spec/controllers/js_base_spec.rb +10 -4
- data/spec/jobs/source_job_spec.rb +1 -1
- data/spec/models/{js_model_spec.rb → js_base_spec.rb} +63 -13
- data/spec/server/server_spec.rb +20 -0
- data/spec/server/stats_spec.rb +7 -17
- data/spec/source_adapter_spec.rb +6 -0
- data/spec/source_sync_spec.rb +219 -54
- data/spec/spec_helper.rb +8 -13
- data/spec/store_spec.rb +6 -4
- data/spec/test_methods_spec.rb +4 -4
- metadata +14 -27
- data/commands/execute.rb +0 -48
- data/commands/utilities/utilities.rb +0 -6
- data/generators/templates/application/controllers/application_controller.rb +0 -17
- data/lib/rhoconnect/js_adapter.rb +0 -79
@@ -1,14 +1,16 @@
|
|
1
1
|
Execute.define_task do
|
2
2
|
desc "config", "Config", :hide => true
|
3
3
|
def config(settings_file=false)
|
4
|
+
require 'uri'
|
5
|
+
require 'yaml'
|
4
6
|
if File.exist?(File.join('settings','settings.yml'))
|
5
|
-
settings =
|
7
|
+
settings = YAML.load_file(File.join('settings','settings.yml'))
|
6
8
|
rackup_config = "config.ru"
|
7
9
|
elsif settings_file
|
8
|
-
settings =
|
10
|
+
settings = YAML.load_file(settings_file)
|
9
11
|
rackup_config = File.join(File.dirname(__FILE__), '..', 'utilities', 'blank_app.ru')
|
10
12
|
elsif File.exist?(File.join(ENV['HOME'], '.rhoconnect.yml'))
|
11
|
-
settings =
|
13
|
+
settings = YAML.load_file(File.join(ENV['HOME'], '.rhoconnect.yml'))
|
12
14
|
rackup_config = File.join(File.dirname(__FILE__), '..', 'utilities', 'blank_app.ru')
|
13
15
|
else
|
14
16
|
options = { :syncserver => "http://localhost:#{RHOCONNECT_PORT}",
|
@@ -16,7 +18,9 @@ Execute.define_task do
|
|
16
18
|
settings = { :development => options, :test => options, :production => options }
|
17
19
|
rackup_config = File.join(File.dirname(__FILE__), '..', 'utilities', 'blank_app.ru')
|
18
20
|
end
|
19
|
-
|
21
|
+
|
22
|
+
environment = (ENV['RHO_ENV'] || ENV['RACK_ENV'] || :development).to_sym # FIXME:
|
23
|
+
settings = settings[environment]
|
20
24
|
# syncserver settings
|
21
25
|
uri = URI.parse(settings[:syncserver])
|
22
26
|
port = uri.port unless port
|
@@ -1,6 +1,7 @@
|
|
1
1
|
Execute.define_task do
|
2
2
|
desc "get-token", "Fetch current api token from rhoconnect"
|
3
3
|
def get_token(save = true)
|
4
|
+
require 'rest_client'
|
4
5
|
url = config[:syncserver]
|
5
6
|
password = ''
|
6
7
|
begin
|
@@ -15,7 +16,8 @@ desc "get-token", "Fetch current api token from rhoconnect"
|
|
15
16
|
begin
|
16
17
|
token = RestClient.post("#{url}/rc/v1/system/login",
|
17
18
|
{ :login => 'rhoadmin', :password => password }.to_json, :content_type => :json)
|
18
|
-
rescue
|
19
|
+
rescue Exception => e
|
20
|
+
puts e.message
|
19
21
|
puts "Rhoconnect server is not running or invalid credentials."
|
20
22
|
exit
|
21
23
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Execute.define_task do
|
2
|
+
desc "routes", "Prints out routes defined in the application"
|
3
|
+
def routes
|
4
|
+
puts ""
|
5
|
+
puts "\tYour Application [#{Dir.pwd}] has the following routes map:"
|
6
|
+
puts ""
|
7
|
+
start_list = []
|
8
|
+
begin
|
9
|
+
redis_array = config[:redis]
|
10
|
+
redis_array.each do |redis_inst|
|
11
|
+
start_list << redis_inst unless RedisRunner.running?(redis_inst)
|
12
|
+
end
|
13
|
+
RedisRunner.startbg(start_list) unless start_list.empty?
|
14
|
+
|
15
|
+
require 'rhoconnect/application/init'
|
16
|
+
Rhoconnect::Server.set :secret, 'temp_secret'
|
17
|
+
Rhoconnect.url_map.each do |root, controller|
|
18
|
+
puts " #{controller.helpers.class.name}: #{root}"
|
19
|
+
controller.helpers.class.paths.each do |verb, paths|
|
20
|
+
paths.each do |path|
|
21
|
+
puts " --> #{verb.to_s.upcase}\t #{path}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
rescue Exception => e
|
26
|
+
Rhoconnect.log "#{e.inspect}"
|
27
|
+
ensure
|
28
|
+
Rhoconnect.shutdown
|
29
|
+
# TODO: Something is wrong here on Windows!!!
|
30
|
+
# if I use RedisRunner.stop - process hangs for several seconds
|
31
|
+
system("rhoconnect redis-stop") unless start_list.empty?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -2,7 +2,7 @@ Execute.define_task do
|
|
2
2
|
desc "secret", "Generate a cryptographically secure secret session key"
|
3
3
|
def secret
|
4
4
|
begin
|
5
|
-
require 'securerandom'
|
5
|
+
require 'securerandom'
|
6
6
|
puts SecureRandom.hex(64)
|
7
7
|
rescue LoadError
|
8
8
|
puts "Missing secure random generator. Try running `rhoconnect secret` in a rails application instead."
|
@@ -17,8 +17,9 @@ Execute.define_task do
|
|
17
17
|
elsif new_pass == new_pass_confirm
|
18
18
|
puts ""
|
19
19
|
url = config[:syncserver]
|
20
|
-
RestClient.put("#{url}/rc/v1/users/rhoadmin",
|
20
|
+
res = RestClient.put("#{url}/rc/v1/users/rhoadmin",
|
21
21
|
{ :attributes => { :new_password => new_pass }}.to_json, {:content_type => :json, 'X-RhoConnect-API-TOKEN' => token})
|
22
|
+
puts "Admin password is successfully updated" if res.code == 200
|
22
23
|
else
|
23
24
|
puts "\nNew password and confirmation must match."
|
24
25
|
end
|
@@ -8,10 +8,10 @@
|
|
8
8
|
Execute.define_task do
|
9
9
|
desc "startbg [CHILDNAME]", "Start rhoconnect server in bg mode (Rhostudio) - this is an internal command", :hide => true
|
10
10
|
def startbg(childname=nil)
|
11
|
-
cmd = (
|
11
|
+
cmd = "bundle exec rackup -s #{defined?(JRUBY_VERSION) ? 'puma' : 'thin'}"
|
12
12
|
require 'win32/process' if windows?
|
13
|
-
|
14
|
-
p1 = Process.fork {
|
13
|
+
|
14
|
+
p1 = Process.fork {
|
15
15
|
if windows?
|
16
16
|
puts 'Starting rhoconnect ...'
|
17
17
|
system "#{cmd} config.ru -P #{rhoconnect_pid}"
|
@@ -23,7 +23,7 @@ Execute.define_task do
|
|
23
23
|
end
|
24
24
|
}
|
25
25
|
Process.detach(p1)
|
26
|
-
|
26
|
+
|
27
27
|
exit
|
28
28
|
end #startbg
|
29
29
|
end
|
data/commands/rhoconnect/stop.rb
CHANGED
@@ -5,12 +5,12 @@ Execute.define_task do
|
|
5
5
|
File.delete "#{rhoconnect_pid}" if system("FOR /F %A in (#{rhoconnect_pid}) do taskkill /F /PID %A")
|
6
6
|
else
|
7
7
|
if File.exist?("#{rhoconnect_pid}")
|
8
|
-
pid = `cat #{rhoconnect_pid}`
|
8
|
+
pid = `cat #{rhoconnect_pid}`
|
9
9
|
puts "Sending a QUIT signal to process #{pid}"
|
10
10
|
system "kill -3 #{pid}"
|
11
11
|
3.times do
|
12
12
|
sleep 1
|
13
|
-
return if !File.exist?("#{rhoconnect_pid}")
|
13
|
+
return if !File.exist?("#{rhoconnect_pid}")
|
14
14
|
end
|
15
15
|
|
16
16
|
puts "Process #{pid} is still running. Sending a KILL signal to it ..."
|
@@ -2,17 +2,17 @@ Execute.define_task do
|
|
2
2
|
desc "console [environment]", "Run rhoconnect console"
|
3
3
|
def console(environment=nil)
|
4
4
|
ENV['RACK_ENV'] = environment || 'development'
|
5
|
-
|
6
|
-
File.join(Dir.pwd, '
|
7
|
-
File.join(File.dirname(__FILE__), '..', '..', 'generators', 'templates', 'application', '
|
5
|
+
controller_file = (File.exist?(File.join(Dir.pwd, 'controllers', 'ruby', 'application_controller.rb'))) ?
|
6
|
+
File.join(Dir.pwd, 'controllers', 'ruby', 'application_controller.rb') :
|
7
|
+
File.join(File.dirname(__FILE__), '..', '..', 'generators', 'templates', 'application', 'controllers', 'ruby', 'application_controller.rb')
|
8
8
|
|
9
|
-
redis_url = config[:redis]
|
10
9
|
if RedisRunner.running?
|
11
10
|
system "irb -rubygems -r #{File.join(File.dirname(__FILE__),'console_helper')} " +
|
12
11
|
"-r #{File.join(File.dirname(__FILE__), '..', '..', 'lib', 'rhoconnect') } " +
|
13
|
-
"-r #{
|
12
|
+
"-r #{File.join(File.dirname(__FILE__), '..', '..', 'lib', 'rhoconnect', 'server') } " +
|
13
|
+
"-r #{controller_file}"
|
14
14
|
else
|
15
|
-
puts "Redis is not running
|
15
|
+
puts "Redis is not running. Please start it by running 'rhoconnect redis-start' command."
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -65,13 +65,13 @@ class RedisRunner
|
|
65
65
|
def self.startbg(redis_array)
|
66
66
|
if windows?
|
67
67
|
puts "Starting redis in a new window..."
|
68
|
-
system "start #{File.join(redis_home,'redis-server')} #{File.join(redis_home,'redis.conf')}" rescue
|
68
|
+
system "start \"\" #{File.join(redis_home,'redis-server')} #{File.join(redis_home,'redis.conf')}" rescue
|
69
69
|
"redis-server not installed on your path, please install redis."
|
70
70
|
else
|
71
71
|
puts "Starting redis ..."
|
72
72
|
redis_array.each do |cfg|
|
73
73
|
host, port = cfg.split(':')[0..1]
|
74
|
-
system("redis-server --port #{port} &") if RedisRunner.local_ip?(host)
|
74
|
+
system("redis-server --port #{port} > /dev/null 2>&1 &") if RedisRunner.local_ip?(host)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
data/doc/data-partitioning.txt
CHANGED
@@ -9,25 +9,38 @@ The MD is referenced in RhoConnect by a corresponding partition. Source adapter
|
|
9
9
|
|
10
10
|
Likewise, app partitioning stores one copy of the source adapter MD for the entire application (all users and devices share the same data). App partitioning can be particularly useful if you have source adapter models which retrieve large amounts of data that is fixed from user to user, for example a global product catalog. Using app partitioning wherever possible ***greatly reduces*** the amount of data in redis.
|
11
11
|
|
12
|
-
###
|
13
|
-
|
12
|
+
### App Partition
|
13
|
+
Enable app partitioning the same way:
|
14
14
|
|
15
15
|
:::yaml
|
16
16
|
:sources:
|
17
17
|
Product:
|
18
18
|
:poll_interval: 300
|
19
|
-
:partition_type:
|
19
|
+
:partition_type: app
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
Now you have a single copy of the `Product` source adapter dataset for all users.
|
22
|
+
|
23
|
+
### User Partition
|
24
|
+
User partitioning is the default mode for source adapters, however you can explicitly define it in `settings/settings.yml` with:
|
23
25
|
|
24
26
|
:::yaml
|
25
27
|
:sources:
|
26
28
|
Product:
|
27
29
|
:poll_interval: 300
|
28
|
-
:partition_type:
|
30
|
+
:partition_type: user
|
29
31
|
|
30
|
-
|
32
|
+
### Custom User partitioning
|
33
|
+
Sometimes, different groups of users share the common source data. To leverage this, you can implement the following method in your Source Adapter Model to provide custom partition names for the users with shared data. In this case, RhoConnect will store the data for MD of the grouped users under your custom name, which will reduce the memory footprint in Redis. From this standpoint, `app` partition is the edge-case of the custom user partitioning where all users share the same data for the particular source.
|
34
|
+
|
35
|
+
To use the custom user partitioning, implement the following class method in your Source Adapter's model:
|
36
|
+
|
37
|
+
:::ruby
|
38
|
+
class Product < Rhoconnect::Model::Base
|
39
|
+
# group users by the first letter
|
40
|
+
def self.partition_name(user_id)
|
41
|
+
return user_id[0]
|
42
|
+
end
|
43
|
+
end
|
31
44
|
|
32
45
|
## Pass Through
|
33
46
|
RhoConnect provides a simple way to keep data out of redis. If you have sensitive data that you do not want saved in redis, add the `pass_through` option in settings/settings.yml for each source:
|
@@ -1,23 +1,52 @@
|
|
1
1
|
Extending RhoConnect Application with custom routes
|
2
2
|
===
|
3
3
|
|
4
|
+
Adding custom routes to a Controller
|
5
|
+
---
|
6
|
+
|
4
7
|
You can provide custom routes support in your RhoConnect application and utilize all the powerful features of the typical Sinatra app. To do this, simply define your routes in the corresponding controller class. Endpoint URL for your routes will be relative to the controller's root.
|
5
8
|
|
6
|
-
The following example illustrates how to add a sample `my_custom_route
|
9
|
+
The following example illustrates how to add a sample `my_custom_route` to the Product Controller:
|
7
10
|
|
8
11
|
:::ruby
|
9
|
-
class
|
12
|
+
class ProductController < Rhoconnect::Controller::Base
|
10
13
|
get '/my_custom_route', :login_required => true do
|
11
14
|
send_file 'public/my_file.png'
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
15
|
-
This route will have the following URL : GET '/app/
|
18
|
+
This route will have the following URL : GET '/app/v1/Product/my_custom_route'
|
16
19
|
|
17
|
-
The above custom route implementation will respond to client's GET request, verifies the current_user (which will be extracted from the
|
20
|
+
The above custom route implementation will respond to client's GET request, verifies the current_user (which will be extracted from the RhoConnect session cookie and checked for validity by the `:login_required` condition)
|
18
21
|
and returns a static PNG file (which can be later used in BLOB syncs)
|
19
22
|
|
20
|
-
|
23
|
+
Routes declaration order
|
24
|
+
---
|
25
|
+
|
26
|
+
At run-time all requests are being matched against the declared routes to find the appropriate route handler. Sinatra matches the routes sequentially in the order of their declaration until the first matching route signature is found. This can lead to a possibility where the declared route can hide subsequent routes. For example:
|
27
|
+
|
28
|
+
:::ruby
|
29
|
+
class ProductController < Rhoconnect::Controller::Base
|
30
|
+
register Rhoconnect::Handler::Sync
|
31
|
+
|
32
|
+
delete '/my_custom_route_delete', :login_required => true do
|
33
|
+
# do something here
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
In the above example, user's custom route will never be called, because `register Rhoconnect::Handler::Sync` (which is responsible for [adding pre-defined SYNC routes](/rhoconnectapi/source-adapter-controller-api-ruby#register Rhoconnect::Handler::Sync)) will add `DELETE '/:id'` route.
|
38
|
+
As a result, ProductController will have the following routes map:
|
39
|
+
|
40
|
+
* 1) DELETE '/:id'
|
41
|
+
* 2) DELETE '/my_custom_route_delete'
|
42
|
+
|
43
|
+
At run-time, Controller will try to match `DELETE /my_custom_route_delete` with the FIRST appropriate route declaration - and, it will find that `DELETE '/:id'` is a good match. Controller will treat string `my_custom_route_delete` as an instance of an `:id` parameter. So, the user-defined custom route will never be called - it is being hidden by the more-generic route that has been declared first in order.
|
44
|
+
|
45
|
+
In summary, you should always specify more-specific routes first followed by the more-generic routes. In the above example, moving the custom route's declaration above the line 'register Rhoconnect::Handler::Sync' will make the custom route match happening before the parametrized route. Alternatively, you can write your routes using "as unique as possible" signatures, i.e. in the above example, declaring the route as `DELETE '/my/custom_route/delete'` will not match any other route declaration.
|
46
|
+
|
47
|
+
NOTE: You can always see the routes that are available in your application using the `rhoconnect routes` command.
|
48
|
+
|
49
|
+
Using custom routes in BLOB syncs
|
21
50
|
---
|
22
51
|
|
23
52
|
After you create the new custom routes you can reference and use them from standard Rhodes applications.
|
@@ -52,11 +81,11 @@ Then, modify your RhoConnect source adapter model (in `rhoconnect_app/models/rub
|
|
52
81
|
|
53
82
|
@result={}
|
54
83
|
parsed.each do |item|
|
55
|
-
item["product"]["my_custom_field-rhoblob"] = "http://localhost:9292/app/
|
84
|
+
item["product"]["my_custom_field-rhoblob"] = "http://localhost:9292/app/v1/Product/my_custom_route"
|
56
85
|
@result[item["product"]["id"].to_s] = item["product"]
|
57
86
|
end if parsed
|
58
87
|
end
|
59
88
|
|
60
89
|
This way, your `my_custom_field` would be getting the BLOB image data from the custom route defined in your RhoConnect Source Adapter Controller.
|
61
90
|
|
62
|
-
For more information on BLOB syncs, see [this section](
|
91
|
+
For more information on BLOB syncs, see [this section](/rhoconnect/blob-sync).
|
@@ -91,13 +91,13 @@ You can also configure more advanced settings in RhoConnect push by creating a c
|
|
91
91
|
{
|
92
92
|
"httpSecure": "n",
|
93
93
|
"devAuthHost": "localhost",
|
94
|
-
"devAuthUrl": "/rc/v1/app/
|
94
|
+
"devAuthUrl": "/rc/v1/app/rps_login",
|
95
95
|
"devAuthPort": "9292",
|
96
96
|
"userAuthHost": "localhost",
|
97
|
-
"userAuthUrl": "/rc/v1/app/
|
97
|
+
"userAuthUrl": "/rc/v1/app/rps_login",
|
98
98
|
"userAuthPort": "9292",
|
99
99
|
"appAuthHost": "localhost",
|
100
|
-
"appAuthUrl": "/rc/v1/
|
100
|
+
"appAuthUrl": "/rc/v1/system/rps_login",
|
101
101
|
"appAuthPort": "9292",
|
102
102
|
"ansResponseTimeout": "300000",
|
103
103
|
"ansServerPort": "8675",
|
data/doc/source-adapters-js.txt
CHANGED
@@ -55,6 +55,8 @@ Its purpose is to define the RhoConnect application HTTP end point and to regist
|
|
55
55
|
The generated source adapter's model file (in this case, `models/product.js`) is similar to the code listing below:
|
56
56
|
|
57
57
|
:::javascript
|
58
|
+
var rc = require('rhoconnect_helpers');
|
59
|
+
|
58
60
|
var Product = function(){
|
59
61
|
|
60
62
|
this.login = function(resp){
|
@@ -98,10 +100,20 @@ The generated source adapter's model file (in this case, `models/product.js`) is
|
|
98
100
|
// TODO: Logout from the data source if necessary.
|
99
101
|
resp.send(true);
|
100
102
|
};
|
103
|
+
|
104
|
+
this.storeBlob = function(resp){
|
105
|
+
// TODO: Handle post requests for blobs here.
|
106
|
+
// Reference the blob object's path with resp.params.path.
|
107
|
+
new rc.Exception(
|
108
|
+
resp, "Please provide some code to handle blobs if you are using them."
|
109
|
+
);
|
110
|
+
};
|
101
111
|
};
|
102
112
|
|
103
113
|
module.exports = new Product();
|
104
114
|
|
115
|
+
In this model you will need to implement the functions necessary for the create, query, update and delete sync operations. Optionally you may implement functions for login, logoff and storeBlob (if your application uses blobs).
|
116
|
+
|
105
117
|
## Source Adapter API
|
106
118
|
|
107
119
|
### Source Adapter Controller API
|
@@ -131,7 +143,7 @@ You can write the following methods for your source adapter model. These methods
|
|
131
143
|
* [getData](/rhoconnectapi/source-adapter-model-api-js#getdataresp-callback) - Get the model document data from Store.
|
132
144
|
* [currentUser](/rhoconnectapi/source-adapter-model-api-js#currentuser) - Returns the current user who called the adapter's model.
|
133
145
|
* [storeBlob](/rhoconnectapi/source-adapter-model-api-js#storeblob) - Save the incoming blob data into permanent storage for the future processing.
|
134
|
-
|
146
|
+
* [partitionName](/rhoconnectapi/source-adapter-model-api-js#partitionname) - Provide a custom user partition for a model.
|
135
147
|
|
136
148
|
### Request API
|
137
149
|
The [request object](/rhoconnectapi/source-adapter-request-api-js) contains information about the HTTP request the app is receiving.
|
@@ -157,6 +169,17 @@ RhoConnect provides a simple [redis interface](/rhoconnectapi/source-adapter-sto
|
|
157
169
|
* [getData](/rhoconnectapi/source-adapter-store-api-js#getdatarespcallback) - Retrieve an array or hash from redis.
|
158
170
|
* [putData](/rhoconnectapi/source-adapter-store-api-js#putdatarespcallback) - Add an array or hash to redis.
|
159
171
|
|
172
|
+
## Exception API
|
173
|
+
Use the following exception types in your JavaScript application to handle any error conditions. Don't worry, if your application throws an uncaught exception, the base `RhoConnect::Model::Exception` will automatically be used.
|
174
|
+
|
175
|
+
* [Exception](/rhoconnectapi/source-adapter-exception-api-js#exceptionresponse-message) - Raise a `RhoConnect::Model::Exception`.
|
176
|
+
* [LoginException](/rhoconnectapi/source-adapter-exception-api-js#loginexceptionresponse-message) - Raise a `RhoConnect::Model::LoginException`.
|
177
|
+
* [LogoffException](/rhoconnectapi/source-adapter-exception-api-js#logoffexceptionresponse-message) - Raise a `RhoConnect::Model::LogoffException`.
|
178
|
+
* [ServerTimeoutException](/rhoconnectapi/source-adapter-exception-api-js#servertimeoutexceptionresponse-message) - Raise a `RhoConnect::Model::ServerTimeoutException`.
|
179
|
+
* [ServerErrorException](/rhoconnectapi/source-adapter-exception-api-js#servererrorexceptionresponse-message) - Raise a `RhoConnect::Model::ServerErrorException`.
|
180
|
+
* [ObjectConflictErrorException](/rhoconnectapi/source-adapter-exception-api-js#objectconflicterrorexceptionresponse-message) - Raise a `RhoConnect::Model::ObjectConflictErrorException`.
|
181
|
+
|
182
|
+
|
160
183
|
## Sample Model
|
161
184
|
Here's a complete example of how the completed [product model might look](https://gist.github.com/larsburgess/87753882f3a4a366b48b):
|
162
185
|
|
@@ -196,7 +219,10 @@ Here's a complete example of how the completed [product model might look](https:
|
|
196
219
|
host: host,
|
197
220
|
path: '/products.json',
|
198
221
|
method: 'POST',
|
199
|
-
headers: {
|
222
|
+
headers: {
|
223
|
+
'Content-Type': 'application/json',
|
224
|
+
'Content-Length': postData.length
|
225
|
+
}
|
200
226
|
};
|
201
227
|
var req = http.request(options, function(res){
|
202
228
|
res.on('data', function(chunk){
|
@@ -220,7 +246,10 @@ Here's a complete example of how the completed [product model might look](https:
|
|
220
246
|
host: host,
|
221
247
|
path: '/products/' + objId + '.json',
|
222
248
|
method: 'PUT',
|
223
|
-
headers: {
|
249
|
+
headers: {
|
250
|
+
'Content-Type': 'application/json',
|
251
|
+
'Content-Length': putData.length
|
252
|
+
}
|
224
253
|
};
|
225
254
|
var req = http.request(options, function(res){
|
226
255
|
res.on('data', function(){});
|
@@ -258,6 +287,14 @@ Here's a complete example of how the completed [product model might look](https:
|
|
258
287
|
this.logoff = function(resp){
|
259
288
|
resp.send(true);
|
260
289
|
};
|
290
|
+
|
291
|
+
this.storeBlob = function(resp){
|
292
|
+
// TODO: Handle post requests for blobs here.
|
293
|
+
// Reference the blob object's path with resp.params.path.
|
294
|
+
new rc.Exception(
|
295
|
+
resp, "Please provide some code to handle blobs if you are using them."
|
296
|
+
);
|
297
|
+
};
|
261
298
|
};
|
262
299
|
|
263
|
-
module.exports = new Product();
|
300
|
+
module.exports = new Product();
|
data/doc/source-adapters.txt
CHANGED
@@ -130,6 +130,8 @@ During generation of the model, the `settings/settings.yml` file will be updated
|
|
130
130
|
|
131
131
|
You can use the following methods and techniques inside of your source adapter controller.
|
132
132
|
|
133
|
+
* [register Rhoconnect::EndPoint](/rhoconnectapi/source-adapter-controller-api-ruby#register Rhoconnect::EndPoint) - adds Controller into the application's URL Map.
|
134
|
+
* [resgiter Rhoconnect::Handler::Sync](/rhoconnectapi/source-adapter-controller-api-ruby#register Rhoconnect::Handler::Sync) - adds SYNC routes to your Controller.
|
133
135
|
* [@model](/rhoconnectapi/source-adapter-controller-api-ruby#@model) - Access to the source adapter's model instance.
|
134
136
|
* [params,request,response](/rhoconnectapi/source-adapter-controller-api-ruby#sinatra-context) - Allows to use any standard Sinatra context objects.
|
135
137
|
* [Sinatra framework](/rhoconnectapi/source-adapter-controller-api-ruby#sinatra-features) - Allows to use any standard Sinatra features.
|
@@ -148,6 +150,7 @@ You can write the following methods for your source adapter model. These methods
|
|
148
150
|
* [current_user](/rhoconnectapi/source-adapter-model-api-ruby#currentuser) - Returns the current user which called the adapter's model.
|
149
151
|
* [stash_result](/rhoconnectapi/source-adapter-model-api-ruby#stashresult) - Saves the current state of `@result` to redis and assigns it to `nil`.
|
150
152
|
* [store_blob](/rhoconnectapi/source-adapter-model-api-ruby#store_blob) - Save the incoming blob data into permanent storage for the future processing.
|
153
|
+
* [self.partition_name](/rhoconnectapi/source-adapter-model-api-ruby#self.partition_name) - Implement this class method to provide custom user partition.
|
151
154
|
* [get_data](/rhoconnectapi/source-adapter-model-api-ruby#get_data) - Get the model document data from Store.
|
152
155
|
|
153
156
|
## Data Partitioning
|