vayacondios-server 0.2.11 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -1
- data/.travis.yml +2 -0
- data/Gemfile +15 -9
- data/LICENSE.md +2 -6
- data/Procfile +1 -1
- data/README.md +656 -111
- data/Rakefile +89 -6
- data/bin/vcd +10 -0
- data/bin/vcd-server +8 -0
- data/config/database.yml +6 -0
- data/config/spec.example.yml +18 -0
- data/config/vayacondios.example.yml +15 -0
- data/config/vcd-server.rb +37 -0
- data/examples/configuration.rb +56 -0
- data/examples/event_stream.rb +19 -0
- data/examples/simple.rb +61 -0
- data/features/event.feature +319 -0
- data/features/events.feature +208 -0
- data/features/stash.feature +840 -0
- data/features/stashes.feature +492 -0
- data/features/step_definitions/stash_steps.rb +113 -0
- data/features/stream.feature +30 -0
- data/features/support/em.rb +14 -0
- data/features/support/env.rb +13 -0
- data/lib/vayacondios/configuration.rb +63 -0
- data/lib/vayacondios/server/api.rb +126 -0
- data/lib/vayacondios/server/api_options.rb +56 -0
- data/lib/vayacondios/server/configuration.rb +23 -0
- data/lib/vayacondios/server/driver.rb +71 -0
- data/lib/vayacondios/server/drivers/mongo.rb +126 -0
- data/lib/vayacondios/server/handlers/document_handler.rb +81 -0
- data/lib/vayacondios/server/handlers/event_handler.rb +31 -26
- data/lib/vayacondios/server/handlers/events_handler.rb +31 -0
- data/lib/vayacondios/server/handlers/stash_handler.rb +69 -0
- data/lib/vayacondios/server/handlers/stashes_handler.rb +49 -0
- data/lib/vayacondios/server/handlers/stream_handler.rb +39 -0
- data/lib/vayacondios/server/models/document.rb +87 -0
- data/lib/vayacondios/server/models/event.rb +198 -0
- data/lib/vayacondios/server/models/stash.rb +100 -0
- data/lib/vayacondios/server.rb +35 -0
- data/lib/vayacondios-server.rb +19 -13
- data/lib/vayacondios.rb +22 -0
- data/pom.xml +124 -4
- data/spec/configuration_spec.rb +41 -0
- data/spec/server/api_options_spec.rb +32 -0
- data/spec/server/api_spec.rb +279 -0
- data/spec/server/configuration_spec.rb +27 -0
- data/spec/server/drivers/mongo_spec.rb +107 -0
- data/spec/server/handlers/event_handler_spec.rb +62 -0
- data/spec/server/handlers/events_handler_spec.rb +51 -0
- data/spec/server/handlers/stash_handler_spec.rb +68 -0
- data/spec/server/handlers/stashes_handler_spec.rb +50 -0
- data/spec/server/handlers/stream_handler_spec.rb +5 -0
- data/spec/server/models/document_spec.rb +9 -0
- data/spec/server/models/event_spec.rb +185 -0
- data/spec/server/models/stash_spec.rb +95 -0
- data/spec/spec_helper.rb +23 -3
- data/spec/support/database_helper.rb +42 -0
- data/spec/support/log_helper.rb +19 -0
- data/spec/support/shared_context_for_events.rb +22 -0
- data/spec/support/shared_context_for_stashes.rb +24 -0
- data/spec/support/shared_examples_for_handlers.rb +32 -0
- data/src/main/java/com/infochimps/vayacondios/BaseClient.java +342 -0
- data/src/main/java/com/infochimps/vayacondios/HTTPClient.java +426 -0
- data/src/main/java/com/infochimps/vayacondios/VayacondiosClient.java +487 -65
- data/src/main/java/com/infochimps/vayacondios/test/IntegrationTest.java +3 -0
- data/src/test/java/com/infochimps/vayacondios/BaseClientTest.java +50 -0
- data/src/test/java/com/infochimps/vayacondios/HTTPClientIT.java +267 -0
- data/vayacondios-server.gemspec +9 -9
- metadata +127 -122
- checksums.yaml +0 -15
- data/.rspec +0 -2
- data/.yardopts +0 -10
- data/Guardfile +0 -41
- data/app/http_shim.rb +0 -71
- data/bin/vcd.sh +0 -27
- data/config/http_shim.rb +0 -43
- data/config/vayacondios.example.yaml +0 -7
- data/config/vayacondios.yaml +0 -7
- data/examples/java/ItemSetTest.java +0 -76
- data/lib/tasks/publish.rake +0 -23
- data/lib/tasks/spec.rake +0 -11
- data/lib/tasks/yard.rake +0 -2
- data/lib/vayacondios/client/config.rb +0 -7
- data/lib/vayacondios/client/configliere.rb +0 -38
- data/lib/vayacondios/client/cube_client.rb +0 -39
- data/lib/vayacondios/client/http_client.rb +0 -49
- data/lib/vayacondios/client/itemset.rb +0 -130
- data/lib/vayacondios/client/legacy_switch.rb +0 -43
- data/lib/vayacondios/client/notifier.rb +0 -123
- data/lib/vayacondios/client/zabbix_client.rb +0 -148
- data/lib/vayacondios/legacy_switch.rb +0 -43
- data/lib/vayacondios/server/errors/bad_request.rb +0 -6
- data/lib/vayacondios/server/errors/not_found.rb +0 -6
- data/lib/vayacondios/server/handlers/config_handler.rb +0 -32
- data/lib/vayacondios/server/handlers/itemset_handler.rb +0 -60
- data/lib/vayacondios/server/legacy_switch.rb +0 -43
- data/lib/vayacondios/server/model/config_document.rb +0 -89
- data/lib/vayacondios/server/model/document.rb +0 -25
- data/lib/vayacondios/server/model/event_document.rb +0 -94
- data/lib/vayacondios/server/model/itemset_document.rb +0 -126
- data/lib/vayacondios/server/rack/extract_methods.rb +0 -35
- data/lib/vayacondios/server/rack/jsonize.rb +0 -43
- data/lib/vayacondios/server/rack/params.rb +0 -50
- data/lib/vayacondios/server/rack/path.rb +0 -23
- data/lib/vayacondios/server/rack/path_validation.rb +0 -22
- data/lib/vayacondios/version.rb +0 -3
- data/lib/vayacondios-client.rb +0 -22
- data/scripts/hadoop_monitor/configurable.rb +0 -66
- data/scripts/hadoop_monitor/hadoop_attempt_scraper.rb +0 -45
- data/scripts/hadoop_monitor/hadoop_client.rb +0 -273
- data/scripts/hadoop_monitor/hadoop_monitor.rb +0 -101
- data/scripts/hadoop_monitor/hadoopable.rb +0 -65
- data/scripts/hadoop_monitor/machine_monitor.rb +0 -115
- data/scripts/s3_cataloger/buckets +0 -33
- data/scripts/s3_cataloger/foreach_bucket +0 -88
- data/scripts/s3_cataloger/parse_ls.py +0 -391
- data/spec/client/itemset_legacy_spec.rb +0 -55
- data/spec/client/itemset_spec.rb +0 -60
- data/spec/client/notifier_spec.rb +0 -120
- data/spec/server/config_spec.rb +0 -113
- data/spec/server/event_spec.rb +0 -103
- data/spec/server/itemset_legacy_spec.rb +0 -320
- data/spec/server/itemset_spec.rb +0 -317
- data/spec/server/rack/extract_methods_spec.rb +0 -60
- data/spec/server/rack/path_spec.rb +0 -36
- data/spec/server/rack/path_validation_spec.rb +0 -22
- data/spec/server/server_spec.rb +0 -20
- data/spec/support/mongo_cleaner.rb +0 -32
- data/src/main/java/ItemSetTest.java +0 -76
- data/src/main/java/com/infochimps/util/CurrentClass.java +0 -26
- data/src/main/java/com/infochimps/util/DebugUtil.java +0 -38
- data/src/main/java/com/infochimps/util/HttpHelper.java +0 -181
- data/src/main/java/com/infochimps/vayacondios/ItemSets.java +0 -373
- data/src/main/java/com/infochimps/vayacondios/LinkToVCD.java +0 -18
- data/src/main/java/com/infochimps/vayacondios/MemoryVCDShim.java +0 -84
- data/src/main/java/com/infochimps/vayacondios/Organization.java +0 -62
- data/src/main/java/com/infochimps/vayacondios/PathBuilder.java +0 -13
- data/src/main/java/com/infochimps/vayacondios/StandardVCDLink.java +0 -218
- data/src/main/java/com/infochimps/vayacondios/VCDIntegrationTest.java +0 -108
- data/src/test/java/com/infochimps/vayacondios/TestVayacondiosInMemory.java +0 -78
- data/vayacondios-client.gemspec +0 -25
@@ -0,0 +1,63 @@
|
|
1
|
+
module Vayacondios
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
attr_accessor :base_filename, :load_order
|
5
|
+
|
6
|
+
def initialize(base_fname = nil)
|
7
|
+
@base_filename = base_fname || 'vayacondios.yml'
|
8
|
+
@load_order = %w[ global project ]
|
9
|
+
@settings = Configliere::Param.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def defaults
|
13
|
+
Hash.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def global
|
17
|
+
File.join('/etc/vayacondios', base_filename)
|
18
|
+
end
|
19
|
+
|
20
|
+
def project
|
21
|
+
File.join(ENV['PWD'], 'config', base_filename)
|
22
|
+
end
|
23
|
+
|
24
|
+
def overlay(conf = nil)
|
25
|
+
@overlay = conf unless conf.nil?
|
26
|
+
@overlay
|
27
|
+
end
|
28
|
+
|
29
|
+
def resolved?
|
30
|
+
!!@resolved
|
31
|
+
end
|
32
|
+
|
33
|
+
def resolved_settings
|
34
|
+
resolve!
|
35
|
+
@resolved_settings.dup
|
36
|
+
end
|
37
|
+
|
38
|
+
def [] handle
|
39
|
+
resolved_settings[handle.to_sym]
|
40
|
+
end
|
41
|
+
|
42
|
+
def apply_all
|
43
|
+
scopes = load_order.dup.unshift(:defaults).push(:overlay)
|
44
|
+
scopes.each do |scope|
|
45
|
+
conf = send scope
|
46
|
+
if conf.is_a? String
|
47
|
+
@settings.read_yaml File.read(conf) if File.readable?(conf)
|
48
|
+
elsif conf.is_a? Hash
|
49
|
+
@settings.deep_merge! conf
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def resolve!
|
55
|
+
unless resolved?
|
56
|
+
apply_all
|
57
|
+
@resolved_settings = @settings.to_hash.symbolize_keys
|
58
|
+
@resolved = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'vayacondios-server'
|
2
|
+
|
3
|
+
module Vayacondios::Server
|
4
|
+
|
5
|
+
# Implements the Vayacondios server API.
|
6
|
+
#
|
7
|
+
# ## Setup
|
8
|
+
#
|
9
|
+
# Once the Goliath server has booted, this class is handed control
|
10
|
+
# to process web requests. It uses a set of Rack-aware and
|
11
|
+
# Goliath-friendly plugins to accomplish some of the edges stuff
|
12
|
+
# like routing, parsing params, validating, &c.
|
13
|
+
#
|
14
|
+
# ## Request Loop
|
15
|
+
#
|
16
|
+
# When handling an actual request, it has to do four things:
|
17
|
+
#
|
18
|
+
# * determine which handler class to instantiate to handle the request
|
19
|
+
# * determine the full set of params contained in the request
|
20
|
+
# * call the appropriate method on the new handler, passing in these params
|
21
|
+
# * handle any errors that bubble up
|
22
|
+
#
|
23
|
+
# ## Configuration
|
24
|
+
#
|
25
|
+
# Goliath is kind of weirdly hard to configure nicely. This class
|
26
|
+
# is also required to define an #options_parser method which is
|
27
|
+
# momentarily handed control at bootup time to interpret options
|
28
|
+
# passed to the `vcd-server` program.
|
29
|
+
#
|
30
|
+
# It **simultaneously** is required to read a configuration file
|
31
|
+
# from disk. This configuration file is aware of the Rack
|
32
|
+
# environment the code is running in so it can take
|
33
|
+
# environment-specific actions like creating single-connections in
|
34
|
+
# test/development but using a pool of shared connections in
|
35
|
+
# production mode. The default file is located in the Vayacondios
|
36
|
+
# source distribution at `config/vcd-server.rb`.
|
37
|
+
#
|
38
|
+
class Api < Goliath::API
|
39
|
+
include ApiOptions
|
40
|
+
|
41
|
+
plugin Goliath::Chimp::Plugin::ActivityMonitor, window: 30
|
42
|
+
|
43
|
+
use Goliath::Rack::Heartbeat
|
44
|
+
use Goliath::Chimp::Rack::Formatters::JSON
|
45
|
+
use Goliath::Chimp::Rack::ForceContentType, 'application/json'
|
46
|
+
use Goliath::Rack::Render
|
47
|
+
use Goliath::Rack::Params
|
48
|
+
use Goliath::Chimp::Rack::ApiVersion, Vayacondios::GEM_VERSION, api: 'Vayacondios'
|
49
|
+
use Goliath::Chimp::Rack::ServerMetrics, env_key: { 'routes' => :type }, default: 'other'
|
50
|
+
use Goliath::Rack::Validation::RequestMethod, %w[ GET POST PUT PATCH DELETE ]
|
51
|
+
use Goliath::Chimp::Rack::ControlMethods, 'POST' => :create,
|
52
|
+
'GET' => :retrieve,
|
53
|
+
'PATCH' => :update,
|
54
|
+
'PUT' => :update,
|
55
|
+
'DELETE' => :delete
|
56
|
+
use Goliath::Chimp::Rack::Validation::Routes, /^
|
57
|
+
\/#{Vayacondios::API_VERSION}
|
58
|
+
\/(?<organization>[a-z][-_\w]+)
|
59
|
+
\/(?<type>[-\.\w]+)
|
60
|
+
(\/(?<topic>[-\.\w]+)
|
61
|
+
(\/(?<id>([-\.\w+]\/?)+))?)?
|
62
|
+
$/ix,
|
63
|
+
"/#{Vayacondios::API_VERSION}/<organization>/<type>/<topic>/<id>"
|
64
|
+
use Goliath::Chimp::Rack::Validation::RouteHandler, :type, 'stash' => StashHandler,
|
65
|
+
'stashes' => StashesHandler,
|
66
|
+
'event' => EventHandler,
|
67
|
+
'events' => EventsHandler,
|
68
|
+
'stream' => StreamHandler
|
69
|
+
use Goliath::Chimp::Rack::Validation::RequiredRoutes, :type, 'stash' => :topic,
|
70
|
+
/^events?$/ => :topic,
|
71
|
+
'stream' => :topic
|
72
|
+
|
73
|
+
# The document part of the request, e.g. - params that came
|
74
|
+
# directly from its body.
|
75
|
+
#
|
76
|
+
# Goliath::Rack::Params dumps all non-Hash types that were JSON
|
77
|
+
# parsed under this header. By accessing the #document this way
|
78
|
+
# we allow for non-Hash bodies to be sent as requests.
|
79
|
+
#
|
80
|
+
# @return [Hash,Array,String,Fixnum,nil] any native JSON datatype
|
81
|
+
def document
|
82
|
+
params.has_key?('_json') ? params['_json'] : params
|
83
|
+
end
|
84
|
+
|
85
|
+
# Assign a callback to the stream endpoint. Some of the Rack
|
86
|
+
# logic is recreated because of the way streaming data works.
|
87
|
+
def open_stream(env, hndlr)
|
88
|
+
env[:subscription] = hndlr.stream_data{ |data| env.stream_send MultiJson.dump(data).concat("\n") }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Make sure to remove any outstanding streaming connections
|
92
|
+
# when the client disconnects
|
93
|
+
def on_close env
|
94
|
+
return unless env[:subscription]
|
95
|
+
env.delete(:subscription).close_stream!
|
96
|
+
end
|
97
|
+
|
98
|
+
# Deliver a response for the request.
|
99
|
+
#
|
100
|
+
# Uses the method set by Infochimps::Rack::ControlMethods to
|
101
|
+
# determine which action to call on the handler determined by
|
102
|
+
# Infochimps::Rack::Validation::RouteHandler
|
103
|
+
#
|
104
|
+
# Traps Goliath::Validation::Errors by returning the appropriate
|
105
|
+
# response.
|
106
|
+
#
|
107
|
+
# Traps all other errors by responding with a 500.
|
108
|
+
#
|
109
|
+
# @param [Hash] env the current request environment
|
110
|
+
def response env
|
111
|
+
h = handler.new(logger, db)
|
112
|
+
open_stream(env, h) if routes[:type] == 'stream'
|
113
|
+
body = h.call(control_method, routes, document)
|
114
|
+
[200, {}, body]
|
115
|
+
rescue Goliath::Validation::Error => e
|
116
|
+
return [e.status_code, {}, { error: e.message }]
|
117
|
+
rescue Document::Error => e
|
118
|
+
return [400, {}, { error: e.message }]
|
119
|
+
rescue => e
|
120
|
+
logger.error "#{e.class} -- #{e.message}"
|
121
|
+
e.backtrace.each{ |line| logger.error line }
|
122
|
+
return [500, {}, { error: "#{e.class} -- #{e.message}" }]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Vayacondios::Server
|
2
|
+
module ApiOptions
|
3
|
+
|
4
|
+
def options_parser opts, options
|
5
|
+
opts.banner = <<-BANNER.gsub(/^ {8}/, '').strip
|
6
|
+
usage: vcd-server [--param=value|--param|-p value|-p]
|
7
|
+
|
8
|
+
Vayacondios server lets any system that can speak JSON over HTTP read
|
9
|
+
and write configuration and events.
|
10
|
+
|
11
|
+
It provides the following HTTP endpoints, all of which assume a
|
12
|
+
JSON-encoded request body.
|
13
|
+
|
14
|
+
Events:
|
15
|
+
GET /v2/ORG/event/TOPIC/ID
|
16
|
+
POST /v2/ORG/event/TOPIC[/ID] (announce)
|
17
|
+
DELETE /v2/ORG/event/TOPIC/ID
|
18
|
+
GET /v2/ORG/events/TOPIC (events)
|
19
|
+
DELETE /v2/ORG/events/TOPIC
|
20
|
+
|
21
|
+
Stashes:
|
22
|
+
GET /v2/ORG/stash/TOPIC[/ID] (get)
|
23
|
+
POST /v2/ORG/stash/TOPIC[/ID] (set!)
|
24
|
+
DELETE /v2/ORG/stash/TOPIC[/ID] (delete)
|
25
|
+
GET /v2/ORG/stashes (stashes)
|
26
|
+
DELETE /v2/ORG/stashes (delete_many)
|
27
|
+
BANNER
|
28
|
+
|
29
|
+
opts.separator ''
|
30
|
+
opts.separator 'Database options:'
|
31
|
+
|
32
|
+
options[:database] = {}
|
33
|
+
db_options = options[:database]
|
34
|
+
defaults = DbConfig.defaults[:development]
|
35
|
+
opts.on('-d', '--database.driver NAME', "Database driver (default: #{defaults[:driver]})") do |name|
|
36
|
+
db_options[:driver] = name
|
37
|
+
end
|
38
|
+
opts.on('-h', '--database.host HOST', "Database host (default: #{defaults[:host]})") do |host|
|
39
|
+
db_options[:host] = host
|
40
|
+
end
|
41
|
+
opts.on('-o', '--database.port PORT', Integer, "Database port (default: #{defaults[:port]})") do |port|
|
42
|
+
db_options[:port] = port
|
43
|
+
end
|
44
|
+
opts.on('-D', '--database.name NAME', "Database name (default: #{defaults[:name]})") do |name|
|
45
|
+
db_options[:name] = name
|
46
|
+
end
|
47
|
+
opts.on('-n', '--database.connections NUM', Integer, "Number of database connections to make (default: #{defaults[:connections]}). Production only") do |num|
|
48
|
+
db_options[:connections] = num
|
49
|
+
end
|
50
|
+
|
51
|
+
options[:config] = File.join(Vayacondios.library_dir, 'config/vcd-server.rb')
|
52
|
+
options[:port] = Vayacondios::DEFAULT_SERVER_PORT
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Vayacondios::Server
|
2
|
+
class Configuration < Vayacondios::Configuration
|
3
|
+
|
4
|
+
def defaults
|
5
|
+
{
|
6
|
+
development: {
|
7
|
+
driver: 'mongo',
|
8
|
+
host: 'localhost',
|
9
|
+
port: 27017,
|
10
|
+
name: 'vayacondios_development',
|
11
|
+
connections: 20,
|
12
|
+
}
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def env(handle = nil)
|
17
|
+
handle ||= :development
|
18
|
+
resolved_settings[handle.to_sym] || {}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
DbConfig = Configuration.new('database.yml') unless defined? DbConfig
|
23
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Vayacondios::Server
|
2
|
+
module Driver
|
3
|
+
|
4
|
+
Error = Class.new(StandardError) unless defined? Error
|
5
|
+
|
6
|
+
# Factory methods for drivers
|
7
|
+
class << self
|
8
|
+
def drivers
|
9
|
+
@list ||= {
|
10
|
+
mongo: MongoDriver,
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def load_driver handle
|
15
|
+
drivers = File.expand_path('../drivers', __FILE__)
|
16
|
+
driver_file = File.join(drivers, handle.to_s + '.rb')
|
17
|
+
load driver_file if File.exist? driver_file
|
18
|
+
end
|
19
|
+
|
20
|
+
def retrieve handle
|
21
|
+
load_driver handle
|
22
|
+
drivers[handle.to_sym]
|
23
|
+
end
|
24
|
+
|
25
|
+
def included base
|
26
|
+
base.class_eval{ attr_reader :location, :log } if base.is_a? Class
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Main api entrance method
|
31
|
+
def call(name, request, *options)
|
32
|
+
send("base_#{name}", request)
|
33
|
+
set_location request.location
|
34
|
+
send(name, request.document, *options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def base_insert request
|
38
|
+
log.debug " Processing by #{self.class}#insert"
|
39
|
+
end
|
40
|
+
|
41
|
+
def base_retrieve request
|
42
|
+
log.debug " Processing by #{self.class}#retrieve"
|
43
|
+
end
|
44
|
+
|
45
|
+
def base_update request
|
46
|
+
log.debug " Processing by #{self.class}#update"
|
47
|
+
end
|
48
|
+
|
49
|
+
def base_search request
|
50
|
+
log.debug " Processing by #{self.class}#search"
|
51
|
+
end
|
52
|
+
|
53
|
+
def base_remove request
|
54
|
+
log.debug " Processing by #{self.class}#remove"
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_location loc
|
58
|
+
log.debug " Location: #{loc}"
|
59
|
+
@location = loc
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_log device
|
63
|
+
@log = device
|
64
|
+
end
|
65
|
+
|
66
|
+
# for testing only
|
67
|
+
def unset_location
|
68
|
+
@location = nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Vayacondios::Server
|
2
|
+
class MongoDriver
|
3
|
+
include Driver
|
4
|
+
|
5
|
+
def self.connect(options = {})
|
6
|
+
new(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@log = options[:log]
|
11
|
+
mongo = EM::Mongo::Connection.new(options[:host], options[:port], 1, reconnect_in: 1)
|
12
|
+
@connection = mongo.db options[:name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def connection
|
16
|
+
@connection.collection location
|
17
|
+
end
|
18
|
+
|
19
|
+
# Coerce objects into a BSON::ObjectId representation if possible.
|
20
|
+
#
|
21
|
+
# @param [BSON::ObjectId,Hash,#to_s] id the object to be coerced
|
22
|
+
# @return [BSON::ObjectId] the canonical representation of the ID
|
23
|
+
# @raise [Error] if `id` is a Hash and is missing the `$oid` parameter which is expected in this case
|
24
|
+
# @raise [Error] if the String representation of `id` is blank or empty
|
25
|
+
def format_id id
|
26
|
+
case
|
27
|
+
when id.is_a?(BSON::ObjectId)
|
28
|
+
id
|
29
|
+
when id.is_a?(Hash)
|
30
|
+
raise Error.new("When settings the ID of a #{self.class} with a Hash, an '$oid' key is required") if id['$oid'].nil?
|
31
|
+
format_id(id['$oid'])
|
32
|
+
when !id.to_s.empty?
|
33
|
+
id.to_s.match(/^[a-f0-9]{24}$/) ? BSON::ObjectId(id.to_s) : id.to_s
|
34
|
+
else
|
35
|
+
raise Error.new("A #{self} cannot have a blank or empty ID")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def mongo_prepare doc
|
40
|
+
doc[:_id] = format_id(doc[:_id]) if doc[:_id]
|
41
|
+
doc
|
42
|
+
end
|
43
|
+
|
44
|
+
def mongo_unprepare doc
|
45
|
+
doc['_id'] = doc['_id'].to_s
|
46
|
+
doc.symbolize_keys
|
47
|
+
end
|
48
|
+
|
49
|
+
def selector query
|
50
|
+
sel = { }.tap do |sel|
|
51
|
+
time = query.delete(:_t)
|
52
|
+
sel[:_t] = time.inject({}){ |t, (k,v)| t[('$' + k.to_s).to_sym] = v ; t } if time
|
53
|
+
data = query.delete(:_d)
|
54
|
+
sel.merge! to_dotted_hash(_d: data)
|
55
|
+
end
|
56
|
+
query.merge(sel).compact_blank
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_dotted_hash(hsh, key_string = '')
|
60
|
+
hsh.each_with_object({}) do |(k, v), ret|
|
61
|
+
key = key_string + k.to_s
|
62
|
+
if v.is_a? Hash
|
63
|
+
ret.merge! to_dotted_hash(v, key + '.')
|
64
|
+
else
|
65
|
+
ret[key] = v
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def projector query
|
71
|
+
if query[:sort] == 'time'
|
72
|
+
query[:sort] = '_t'
|
73
|
+
elsif query[:sort].present?
|
74
|
+
query[:sort] = '_d.' + query[:sort]
|
75
|
+
end
|
76
|
+
query[:_reverse] if query.delete(:order) == 'descending'
|
77
|
+
query
|
78
|
+
end
|
79
|
+
|
80
|
+
def search(request, filter, opts)
|
81
|
+
select = selector(filter)
|
82
|
+
log.debug " Selector doc: #{select}"
|
83
|
+
project = projector(opts)
|
84
|
+
log.debug " Projector doc: #{project}"
|
85
|
+
res = connection.find(select, project) || []
|
86
|
+
log.debug " Result: #{res}"
|
87
|
+
res.map{ |res| mongo_unprepare res }
|
88
|
+
end
|
89
|
+
|
90
|
+
def insert request
|
91
|
+
mongo_doc = mongo_prepare request
|
92
|
+
log.debug " Mongo doc: #{mongo_doc}"
|
93
|
+
res = connection.save mongo_doc
|
94
|
+
log.debug " Result: #{res}"
|
95
|
+
res = mongo_doc[:_id] if res == true
|
96
|
+
{ _id: format_id(res).to_s }
|
97
|
+
end
|
98
|
+
|
99
|
+
def retrieve request
|
100
|
+
mongo_doc = mongo_prepare request
|
101
|
+
log.debug " Mongo doc: #{mongo_doc}"
|
102
|
+
res = connection.find_one mongo_doc
|
103
|
+
log.debug " Result: #{res}"
|
104
|
+
return nil if res.nil?
|
105
|
+
mongo_unprepare res
|
106
|
+
end
|
107
|
+
|
108
|
+
def remove(request, filter)
|
109
|
+
mongo_doc = mongo_prepare(request)
|
110
|
+
mongo_doc.merge! selector(filter)
|
111
|
+
log.debug " Mongo doc: #{mongo_doc}"
|
112
|
+
res = connection.remove mongo_doc
|
113
|
+
log.debug " Result: #{res}"
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
|
117
|
+
# for testing only
|
118
|
+
def reset!
|
119
|
+
connection.drop
|
120
|
+
end
|
121
|
+
|
122
|
+
def count
|
123
|
+
connection.count
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Vayacondios::Server
|
2
|
+
|
3
|
+
# Generic handler for all documents.
|
4
|
+
#
|
5
|
+
# Handlers link HTTP applications to document classes.
|
6
|
+
#
|
7
|
+
# @attr [Logger] log the log to use
|
8
|
+
# @attr [Driver] database the database driver
|
9
|
+
class DocumentHandler
|
10
|
+
include Goliath::Chimp::Handler
|
11
|
+
|
12
|
+
attr_reader :log, :database
|
13
|
+
|
14
|
+
# Create a new DocumentHandler.
|
15
|
+
#
|
16
|
+
# @param [Logger] log
|
17
|
+
def initialize(log, db)
|
18
|
+
@log = log
|
19
|
+
@database = db
|
20
|
+
end
|
21
|
+
|
22
|
+
# Search for matching documents.
|
23
|
+
#
|
24
|
+
# @param [Hash] query the search query
|
25
|
+
# @return [Array<Hash>] the matching documents
|
26
|
+
def base_search(params, query)
|
27
|
+
log.debug("Processing by #{self.class}#search")
|
28
|
+
log.debug(" Parameters: #{params.inspect}")
|
29
|
+
log.debug(" Query: #{query.inspect}")
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create a document.
|
33
|
+
#
|
34
|
+
# @param [Hash] params routing information like `organization`, `topic,`, or `id`
|
35
|
+
# @param [Hash] document the body of the document
|
36
|
+
# @return [Hash] the created document
|
37
|
+
def base_create(params, document)
|
38
|
+
log.debug("Processing by #{self.class}#create")
|
39
|
+
log.debug(" Parameters: #{params.inspect}")
|
40
|
+
log.debug(" Document: #{document.inspect}")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Find and show a particular document.
|
44
|
+
#
|
45
|
+
# @param [Hash] params routing information like `organization`, `topic,`, or `id`
|
46
|
+
# @return [Object] the document (or part of a document) that was found
|
47
|
+
def base_retrieve(params, document)
|
48
|
+
log.debug("Processing by #{self.class}#retrieve")
|
49
|
+
log.debug(" Parameters: #{params.inspect}")
|
50
|
+
end
|
51
|
+
|
52
|
+
# Update a document.
|
53
|
+
#
|
54
|
+
# @param [Hash] params routing information like `organization`, `topic,`, or `id`
|
55
|
+
# @param [Hash] document the body of the document
|
56
|
+
# @return [Object] the document (or part a document) that was updated
|
57
|
+
def base_update(params, document)
|
58
|
+
log.debug("Processing by #{self.class}#update")
|
59
|
+
log.debug(" Parameters: #{params.inspect}")
|
60
|
+
log.debug(" Document: #{document.inspect}")
|
61
|
+
end
|
62
|
+
|
63
|
+
# Delete a document.
|
64
|
+
#
|
65
|
+
# @param [Hash] params routing information like `organization`, `topic,`, or `id`
|
66
|
+
# @return [Hash] details about which documents were deleted
|
67
|
+
def base_delete(params, document)
|
68
|
+
log.debug("Processing by #{self.class}#delete")
|
69
|
+
log.debug(" Parameters: #{params.inspect}")
|
70
|
+
end
|
71
|
+
|
72
|
+
def call(name, params, document)
|
73
|
+
send("base_#{name}", params, document)
|
74
|
+
send(name, params, document)
|
75
|
+
end
|
76
|
+
|
77
|
+
def action_successful
|
78
|
+
{ ok: true }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -1,33 +1,38 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# This handler will accept requests to update Event for an organization. All
|
4
|
-
# updates will overwrite an existing document.
|
1
|
+
module Vayacondios::Server
|
2
|
+
class EventHandler < DocumentHandler
|
5
3
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def
|
11
|
-
|
4
|
+
# Create an event.
|
5
|
+
#
|
6
|
+
# @param [Hash] params routing information like `organization`, `topic,`, or `id`
|
7
|
+
# @param [Hash] document the body of the document
|
8
|
+
def create(params, document)
|
9
|
+
Event.create(params, document) do |request|
|
10
|
+
database.call(:insert, request)
|
11
|
+
end
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
existing_document.body
|
14
|
+
# Find and show a particular event.
|
15
|
+
#
|
16
|
+
# @param [Hash] params routing information like `organization`, `topic,`, or `id`
|
17
|
+
# @param [Hash] document the body of the document
|
18
|
+
# @raise [Goliath::Validation::Error] if no event is found. Returns a 404.
|
19
|
+
def retrieve(params, document)
|
20
|
+
Event.find(params) do |request|
|
21
|
+
database.call(:retrieve, request)
|
22
|
+
end or raise Goliath::Validation::NotFoundError.new("Event with topic <#{params[:topic]}> and ID <#{params[:id]}> not found")
|
25
23
|
end
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
# Delete a specific event.
|
26
|
+
#
|
27
|
+
# @param [Hash] params routing information like `organization`, `topic,`, or `id`
|
28
|
+
# @param [Hash] document the body of the document
|
29
|
+
# @raise [Goliath::Validation::Error] if params do not have an id
|
30
|
+
def delete(params, document)
|
31
|
+
raise Goliath::Validation::BadRequestError.new('An <Id> is required to delete an Event') unless params[:id]
|
32
|
+
Event.destroy(params, {}) do |request, options|
|
33
|
+
database.call(:remove, request, options)
|
34
|
+
end
|
35
|
+
action_successful
|
31
36
|
end
|
32
37
|
end
|
33
|
-
end
|
38
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Vayacondios::Server
|
2
|
+
|
3
|
+
# Handles requests against multiple Events.
|
4
|
+
class EventsHandler < DocumentHandler
|
5
|
+
|
6
|
+
# Search for events matching a given query.
|
7
|
+
#
|
8
|
+
# @param [Hash] params routing information like `organization`, `topic,`, or `id`
|
9
|
+
# @param [Hash] query the search query
|
10
|
+
def search(params, query)
|
11
|
+
Event.search(params, query) do |request, filter, opts|
|
12
|
+
database.call(:search, request, filter, opts)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# FIXME
|
17
|
+
# Abstract this into method delegation
|
18
|
+
def base_retrieve(params, query)
|
19
|
+
base_search(params, query)
|
20
|
+
end
|
21
|
+
alias_method :retrieve, :search
|
22
|
+
|
23
|
+
def delete(params, query)
|
24
|
+
Event.destroy(params, query) do |request, opts|
|
25
|
+
database.call(:remove, request, opts)
|
26
|
+
end
|
27
|
+
action_successful
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|