vayacondios-client 0.2.11 → 0.3.0
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/.gitignore +64 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +21 -0
- data/LICENSE.md +95 -0
- data/Procfile +2 -0
- data/README.md +734 -0
- data/Rakefile +93 -0
- data/bin/vcd +10 -0
- data/config/database.yml +6 -0
- data/config/spec.example.yml +18 -0
- data/config/vayacondios.example.yml +15 -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/client/cli.rb +456 -0
- data/lib/vayacondios/client/configuration.rb +13 -0
- data/lib/vayacondios/client/connection.rb +39 -0
- data/lib/vayacondios/client/http_client.rb +6 -42
- data/lib/vayacondios/client/http_methods.rb +85 -0
- data/lib/vayacondios/client.rb +21 -0
- data/lib/vayacondios/configuration.rb +63 -0
- data/lib/vayacondios-client.rb +16 -17
- data/lib/vayacondios.rb +22 -0
- data/pom.xml +168 -0
- data/spec/client/cli_spec.rb +283 -0
- data/spec/client/configuration_spec.rb +11 -0
- data/spec/client/http_client_spec.rb +150 -0
- data/spec/configuration_spec.rb +41 -0
- data/spec/spec_helper.rb +27 -0
- 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 +500 -0
- 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-client.gemspec +25 -0
- metadata +96 -60
- checksums.yaml +0 -15
- 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/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/spec/client/itemset_legacy_spec.rb +0 -55
- data/spec/client/itemset_spec.rb +0 -60
- data/spec/client/notifier_spec.rb +0 -120
@@ -0,0 +1,456 @@
|
|
1
|
+
require 'vayacondios/client'
|
2
|
+
|
3
|
+
module Vayacondios::Client
|
4
|
+
|
5
|
+
# Implements a program `vcd` which makes it easy to interact with
|
6
|
+
# Vayacondios from the command-line or from scripts.
|
7
|
+
#
|
8
|
+
# It makes it easy to use files, pipes, or the command-line itself
|
9
|
+
# to pass data to Vayacondios.
|
10
|
+
#
|
11
|
+
# The 'document' and the 'query' are String inputs passed on the
|
12
|
+
# command line. These String inputs are combined with String inputs
|
13
|
+
# read from files named via command-line options or from STDIN to
|
14
|
+
# create an array of "inputs" for each command to operate on.
|
15
|
+
#
|
16
|
+
# @attr [Vayacondios::HttpClient] client the client used to communicate with the server
|
17
|
+
# @attr [String] topic the topic of the current request
|
18
|
+
# @attr [String] id the ID of the current request
|
19
|
+
# @attr [String] document a document read from a file or passed from the command-line
|
20
|
+
# @attr [String] query a query read from a file or passed from the command-line
|
21
|
+
class CLI
|
22
|
+
|
23
|
+
Error = Class.new(StandardError)
|
24
|
+
|
25
|
+
attr_accessor :client, :topic, :id, :document, :query
|
26
|
+
|
27
|
+
def usage
|
28
|
+
<<-USAGE.gsub(/^ {8}/, '').chomp
|
29
|
+
usage:
|
30
|
+
vcd [ --param=val|--param|-p val|-p ] announce TOPIC [DOCUMENT] [ID]
|
31
|
+
vcd [ --param=val|--param|-p val|-p ] get|set|set!|delete TOPIC ID [DOCUMENT]
|
32
|
+
vcd [ --param=val|--param|-p val|-p ] events TOPIC QUERY
|
33
|
+
vcd [ --param=val|--param|-p val|-p ] stashes QUERY
|
34
|
+
vcd [ --param=val|--param|-p val|-p ] set_many|set_many! QUERY_AND_UPDATE
|
35
|
+
vcd [ --param=val|--param|-p val|-p ] delete_many QUERY
|
36
|
+
USAGE
|
37
|
+
end
|
38
|
+
|
39
|
+
def description
|
40
|
+
<<-DESCRIPTION.gsub(/^ {8}/, '').chomp
|
41
|
+
A command-line Vayacondios client which reads/writes events and
|
42
|
+
configuration to/from a Vayacondios server over HTTP.
|
43
|
+
|
44
|
+
Announce an event like a successful build of your `foo` project on the
|
45
|
+
`foo.build` topic.
|
46
|
+
|
47
|
+
$ vcd announce foo.build '{"version": "1.2.3", "status": "success", "time": 27.56}'
|
48
|
+
|
49
|
+
You can also announce multiple events at once, perhaps of all your
|
50
|
+
projects on the `builds` topic.
|
51
|
+
|
52
|
+
$ cat build_events.json
|
53
|
+
{"project": "foo", "version": "1.2.3", "status": "success", "time": 27.56}
|
54
|
+
{"project": "bar", "version": "0.1.8", "status": "success", "time": 5.22}
|
55
|
+
...
|
56
|
+
$ vcd announce builds --file=build_events.json
|
57
|
+
|
58
|
+
This works in pipelines too
|
59
|
+
|
60
|
+
$ cat build_events.json | vcd announce builds
|
61
|
+
|
62
|
+
Events are assigned their own unique ID unless a third ID argument is
|
63
|
+
provided.
|
64
|
+
|
65
|
+
Events can set their own topics if the contain a `_topic` key and
|
66
|
+
their own IDs if they contain an `_id` key. These fields can
|
67
|
+
themselves be customized, see the --topic_field and --id_field
|
68
|
+
options.
|
69
|
+
|
70
|
+
Stash a configuration value (try the `set!` if you want to
|
71
|
+
completely overwrite the old value with the new instead of merging,
|
72
|
+
which is the default behavior):
|
73
|
+
|
74
|
+
$ vcd set foo admin '{"name": "Bob Jones", "email": "bob@example.com"}'
|
75
|
+
|
76
|
+
Retrieve it again
|
77
|
+
|
78
|
+
$ vcd get foo admin
|
79
|
+
{"name": "Bob Jones", "email": "bob@example.com"}
|
80
|
+
|
81
|
+
Or delete it
|
82
|
+
|
83
|
+
$ vcd delete foo admin
|
84
|
+
|
85
|
+
Getting, setting, or deleting multiple values works the same way as
|
86
|
+
for announcing events: either from a file or over STDIN. Individual
|
87
|
+
records can also set their topic and ID just like events.
|
88
|
+
|
89
|
+
You can change the Vayacondios host, port, or organization at runtime
|
90
|
+
|
91
|
+
$ vcd announce --host=vcd.example.com --organization=myorg foo.build '{"version": "1.2.3", "status": "success", "time": 27.56}'
|
92
|
+
|
93
|
+
As well as through YAML configuration files at
|
94
|
+
|
95
|
+
/etc/vayacondios/vayacondios.yml
|
96
|
+
$PWD/config/vayacondios.yml
|
97
|
+
DESCRIPTION
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the cmdline used for the `vcd` program.
|
101
|
+
#
|
102
|
+
# @return [Configliere::Param]
|
103
|
+
def cmdline
|
104
|
+
return @cmdline if @cmdline
|
105
|
+
c = Configliere::Param.new.use :commandline
|
106
|
+
usg = self.usage
|
107
|
+
c.define_singleton_method(:usage){ usg }
|
108
|
+
c.description = self.description
|
109
|
+
|
110
|
+
c.define :host, flag: 'h', description: "Host for Vayacondios server"
|
111
|
+
c.define :port, flag: 'p', type: Integer, description: "Port for Vayacondios server"
|
112
|
+
c.define :organization, flag: 'o', default: 'vcd', description: "Organization to write data for"
|
113
|
+
c.define :topic_field, default: '_topic', description: "Field used to dynamically determine the topic of a record"
|
114
|
+
c.define :id_field, default: '_id', description: "Field used to dynamically determine the ID of a record"
|
115
|
+
c.define :file, flag: 'f', description: "Read input from file"
|
116
|
+
c.define :log_file, description: "Path to log file (defaut: STDOUT)"
|
117
|
+
c.define :log_level, default: 'INFO', description: "Log level"
|
118
|
+
c.define :pretty, flag: 'p', type: :boolean, default: false, description: "Pretty-print output"
|
119
|
+
c.define :dry_run, type: :boolean, default: false, description: "Don't perform any actual requests"
|
120
|
+
@cmdline = c
|
121
|
+
end
|
122
|
+
|
123
|
+
# Run a new instance of the `vcd` program.
|
124
|
+
#
|
125
|
+
# Instantiates a new instance of CLI and takes it through its
|
126
|
+
# lifecycle.
|
127
|
+
#
|
128
|
+
# @raise [SystemExit] when execution stops either because requests are complete or a fatal error has occured
|
129
|
+
def self.run
|
130
|
+
cli = new
|
131
|
+
begin
|
132
|
+
cli.boot
|
133
|
+
cli.run
|
134
|
+
rescue Error => e
|
135
|
+
$stderr.puts e.message
|
136
|
+
exit(1)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def boot
|
141
|
+
cmdline.resolve!
|
142
|
+
self.client = HttpClient.new(cmdline)
|
143
|
+
log.debug "Created client with settings: #{cmdline}"
|
144
|
+
end
|
145
|
+
|
146
|
+
# Run the desired command.
|
147
|
+
#
|
148
|
+
# @raise [Error] if the desired command is unknown
|
149
|
+
def run
|
150
|
+
command = cmdline.rest.shift
|
151
|
+
return cmdline.dump_help if command.nil?
|
152
|
+
if available_commands.include? command
|
153
|
+
send command
|
154
|
+
else
|
155
|
+
raise Error.new "Unknown command: <#{command}>. Must be one of #{available_commands.inspect}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# == Commands ==
|
161
|
+
#
|
162
|
+
|
163
|
+
def available_commands
|
164
|
+
%w[ announce events clear_events get get_many set set! unset unset_many ]
|
165
|
+
end
|
166
|
+
|
167
|
+
# Announce one or many events.
|
168
|
+
#
|
169
|
+
# Will read the topic as the first argument, the document as the
|
170
|
+
# second, and the ID as the third.
|
171
|
+
#
|
172
|
+
# Will iterate over each input and announce it as an event. It
|
173
|
+
# will attempt to find a topic and ID within each event and will
|
174
|
+
# fall back to using its own as a default.
|
175
|
+
#
|
176
|
+
# @raise [Error] if no topic was given
|
177
|
+
def announce
|
178
|
+
self.topic = cmdline.rest.shift or raise Error.new('Must provide a topic when announcing an event')
|
179
|
+
self.document = cmdline.rest.shift
|
180
|
+
self.id = cmdline.rest.shift
|
181
|
+
raise Error.new("Must provide an event to announce via the second command-line argument, the --file argument, or STDIN.") unless input?
|
182
|
+
inputs do |event|
|
183
|
+
handle_response client.announce(topic_for(event), event, id_for(event))
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Search for events.
|
188
|
+
#
|
189
|
+
# Will read the topic as the first argument and the query as the
|
190
|
+
# second.
|
191
|
+
#
|
192
|
+
# For each input, will send it as a query. Will attempt to find a
|
193
|
+
# topic in the query itself and will fall back to using its own as
|
194
|
+
# a default.
|
195
|
+
#
|
196
|
+
# Results are printed out one per-line.
|
197
|
+
#
|
198
|
+
# @raise [Error] if no topic was given
|
199
|
+
def events
|
200
|
+
self.topic = cmdline.rest.shift or raise Error.new("Must provide a topic when searching for events")
|
201
|
+
self.query = cmdline.rest.shift || '{}'
|
202
|
+
inputs do |query|
|
203
|
+
handle_response client.events(topic_for(query), query)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def clear_events
|
208
|
+
self.topic = cmdline.rest.shift or raise Error.new("Must provide a topic when deleting events")
|
209
|
+
self.query = cmdline.rest.shift || '{}'
|
210
|
+
inputs do |query|
|
211
|
+
handle_response client.clear_events(topic_for(query), query)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
# Get a single stashed value.
|
215
|
+
#
|
216
|
+
# Will read the topic as the first argument as the ID as the
|
217
|
+
# second.
|
218
|
+
#
|
219
|
+
# For each input, will use it to get a stash. Will attempt to
|
220
|
+
# find a topic and ID in the input and will fall back to using its
|
221
|
+
# own as a default.
|
222
|
+
#
|
223
|
+
# @raise [Error] when no topic was given
|
224
|
+
def get
|
225
|
+
self.topic = cmdline.rest.shift or raise Error.new('Must provide a topic when getting a stash')
|
226
|
+
self.id = cmdline.rest.shift
|
227
|
+
if input?
|
228
|
+
inputs do |req|
|
229
|
+
handle_response client.get(topic_for(req), id_for(req))
|
230
|
+
end
|
231
|
+
else
|
232
|
+
handle_response client.get(topic, id)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Search for multiple stashed values.
|
237
|
+
#
|
238
|
+
# Will read the query as the first argument.
|
239
|
+
#
|
240
|
+
# For each input will use it as a query.
|
241
|
+
def get_many
|
242
|
+
self.query = (cmdline.rest.shift || '{}')
|
243
|
+
inputs do |query|
|
244
|
+
handle_response client.get_many(query)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Set a value by merging it into a (potentially) existing value.
|
249
|
+
#
|
250
|
+
# Will read the topic as the first argument, ID as the second, and
|
251
|
+
# document as the third.
|
252
|
+
#
|
253
|
+
# For each input, will merge that input. Will attempt to read a
|
254
|
+
# topic and ID for each input and will fall back to its own as a
|
255
|
+
# default.
|
256
|
+
#
|
257
|
+
# @raise [Error] if no topic was given
|
258
|
+
def set
|
259
|
+
self.topic = cmdline.rest.shift or raise Error.new('Must provide a topic when setting a stash')
|
260
|
+
self.document = cmdline.rest.shift
|
261
|
+
self.id = cmdline.rest.shift
|
262
|
+
raise Error.new("Must provide a document to stash via the third command-line argument, the --file argument, or STDIN.") unless input?
|
263
|
+
inputs do |doc|
|
264
|
+
handle_response client.set(topic_for(doc), id_for(doc), doc)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Set a value by overwriting a (potentially) existing value.
|
269
|
+
#
|
270
|
+
# Will read the topic as the first argument, ID as the second, and
|
271
|
+
# document as the third.
|
272
|
+
#
|
273
|
+
# For each input, will write that input. Will attempt to read a
|
274
|
+
# topic and ID for each input and will fall back to its own as a
|
275
|
+
# default.
|
276
|
+
#
|
277
|
+
# @raise [Error] if no topic was given
|
278
|
+
def set!
|
279
|
+
self.topic = cmdline.rest.shift or raise Error.new('Must provide a topic when setting a stash')
|
280
|
+
self.document = cmdline.rest.shift
|
281
|
+
self.id = cmdline.rest.shift
|
282
|
+
raise Error.new("Must provide a document to stash via the third command-line argument, the --file argument, or STDIN.") unless input?
|
283
|
+
inputs do |doc|
|
284
|
+
handle_response client.set!(topic_for(doc), id_for(doc), doc)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
# Delete a stashed value.
|
289
|
+
#
|
290
|
+
# Will read the topic as the first argument and ID as the second.
|
291
|
+
#
|
292
|
+
# For each input, will delete that input. Will attempt to read a
|
293
|
+
# topic and ID for each input and will fall back to its own as a
|
294
|
+
# default.
|
295
|
+
#
|
296
|
+
# @raise [Error] if no topic was given
|
297
|
+
def unset
|
298
|
+
self.topic = cmdline.rest.shift or raise Error.new('Must provide a topic when deleting a stash')
|
299
|
+
self.id = cmdline.rest.shift
|
300
|
+
if input?
|
301
|
+
inputs do |req|
|
302
|
+
handle_response client.unset(topic_for(req), id_for(req))
|
303
|
+
end
|
304
|
+
else
|
305
|
+
handle_response client.unset(topic, id)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# Delete many stashes that match some criteria.
|
310
|
+
#
|
311
|
+
# Each input should be a query Hash.
|
312
|
+
#
|
313
|
+
# @raise [Error] if no input was given
|
314
|
+
def unset_many
|
315
|
+
self.document = cmdline.rest.shift
|
316
|
+
raise Error.new("Must provide a query via the second command-line argument, the --file argument, or STDIN.") unless input?
|
317
|
+
inputs do |query|
|
318
|
+
handle_response client.unset_many(query)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
#
|
323
|
+
# == Inputs ==
|
324
|
+
#
|
325
|
+
|
326
|
+
# Were there any inputs?
|
327
|
+
#
|
328
|
+
# Input is either a document passed on the command-line, a file
|
329
|
+
# requested to be read from the command-line, or is imminent
|
330
|
+
# because data is queuing up on STDIN.
|
331
|
+
#
|
332
|
+
# @return [true, false]
|
333
|
+
def input?
|
334
|
+
document || cmdline.file || input_on_stdin?
|
335
|
+
end
|
336
|
+
|
337
|
+
# For each input, parse each line as a JSON record and yield it to
|
338
|
+
# the given block.
|
339
|
+
#
|
340
|
+
# First the document and/or query will be processed, then any file
|
341
|
+
# named on the command-line, then data over STDIN.
|
342
|
+
#
|
343
|
+
# @yield [input] yields each parsed line from each input
|
344
|
+
# @yieldparam [Hash,Array,String,Numeric,nil] input the input
|
345
|
+
def inputs(&block)
|
346
|
+
raw_inputs do |line|
|
347
|
+
begin
|
348
|
+
yield MultiJson.load(line)
|
349
|
+
rescue => e
|
350
|
+
log.error("#{e.class} -- #{e.message}")
|
351
|
+
$stderr.puts(e.backtrace)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
protected
|
357
|
+
|
358
|
+
def raw_inputs(&block)
|
359
|
+
case
|
360
|
+
when document || query
|
361
|
+
[document, query].compact.each(&block)
|
362
|
+
when cmdline.file
|
363
|
+
File.open(cmdline.file).each(&block)
|
364
|
+
when input_on_stdin?
|
365
|
+
$stdin.each(&block)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# Is there data pending to be read on STDIN?
|
370
|
+
#
|
371
|
+
# @return [true, false]
|
372
|
+
def input_on_stdin?
|
373
|
+
! $stdin.tty?
|
374
|
+
end
|
375
|
+
|
376
|
+
#
|
377
|
+
# == Routing ==
|
378
|
+
#
|
379
|
+
|
380
|
+
# Return the topic for the given document.
|
381
|
+
#
|
382
|
+
# Will read key named by #topic_field if present, otherwise the
|
383
|
+
# default topic.
|
384
|
+
#
|
385
|
+
# @param [Hash] doc
|
386
|
+
# @return [String] topic
|
387
|
+
def topic_for doc
|
388
|
+
return topic unless doc.is_a?(Hash)
|
389
|
+
doc.delete(cmdline.topic_field) || topic
|
390
|
+
end
|
391
|
+
|
392
|
+
# Return the ID for the given document.
|
393
|
+
#
|
394
|
+
# Will read key named by #id_field if present, otherwise the
|
395
|
+
# default ID.
|
396
|
+
#
|
397
|
+
# @param [Hash] doc
|
398
|
+
# @return [String] id
|
399
|
+
def id_for doc
|
400
|
+
raw_id = doc.is_a?(Hash) ? (doc.delete(cmdline.id_field) || id) : id
|
401
|
+
raw_id == '-' ? nil : raw_id
|
402
|
+
end
|
403
|
+
|
404
|
+
#
|
405
|
+
# == Output ==
|
406
|
+
#
|
407
|
+
|
408
|
+
# Handle the JSON response that returns from the Vayacondios
|
409
|
+
# server by printing it out, one response per-line.
|
410
|
+
#
|
411
|
+
# If the `pretty` option was specified then the data will be
|
412
|
+
# pretty-printed first.
|
413
|
+
#
|
414
|
+
# @param [Hash, Array, String, Numeric, nil] doc any of the core JSON types
|
415
|
+
def handle_response res
|
416
|
+
if res.success?
|
417
|
+
display MultiJson.dump(res.body, pretty: cmdline.pretty)
|
418
|
+
else
|
419
|
+
display res.body['error']
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
#
|
424
|
+
# == Log ==
|
425
|
+
#
|
426
|
+
|
427
|
+
def display str
|
428
|
+
puts str
|
429
|
+
end
|
430
|
+
# Where to send log output.
|
431
|
+
#
|
432
|
+
# @return [IO]
|
433
|
+
def log_output
|
434
|
+
case cmdline.log_file
|
435
|
+
when '-' then $stdout
|
436
|
+
when String then File.open(log_file)
|
437
|
+
else
|
438
|
+
$stderr
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
public
|
443
|
+
|
444
|
+
# The log that will be used by the `vcd` program as it runs.
|
445
|
+
#
|
446
|
+
# @return [Logger]
|
447
|
+
def log
|
448
|
+
return @log if @log
|
449
|
+
require 'logger'
|
450
|
+
@log = Logger.new(log_output)
|
451
|
+
@log.level = Logger.const_get(cmdline.log_level.upcase)
|
452
|
+
@log
|
453
|
+
end
|
454
|
+
|
455
|
+
end
|
456
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Vayacondios::Client
|
2
|
+
class Configuration < Vayacondios::Configuration
|
3
|
+
def defaults
|
4
|
+
{
|
5
|
+
host: Vayacondios::DEFAULT_SERVER_ADDRESS,
|
6
|
+
port: Vayacondios::DEFAULT_SERVER_PORT,
|
7
|
+
adapter: :net_http,
|
8
|
+
}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
ConnectionOpts = Configuration.new unless defined? ConnectionOpts
|
13
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Vayacondios::Client
|
2
|
+
module Connection
|
3
|
+
|
4
|
+
def self.base_uri options
|
5
|
+
host = options[:host] || ConnectionOpts[:host]
|
6
|
+
port = options[:port] || ConnectionOpts[:port]
|
7
|
+
"http://#{host}:#{port}/#{Vayacondios::API_VERSION}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.factory(options = {})
|
11
|
+
Faraday.new(base_uri options) do |setup|
|
12
|
+
setup.request :json
|
13
|
+
setup.adapter options[:adapter] || ConnectionOpts[:adapter]
|
14
|
+
setup.response :json, content_type: /\bjson$/
|
15
|
+
if logger = options[:log] || ConnectionOpts[:log]
|
16
|
+
setup.response :logger, logger
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def organization
|
22
|
+
'vcd'
|
23
|
+
end
|
24
|
+
|
25
|
+
def url(handler, topic = nil, id = nil)
|
26
|
+
segments = [organization, handler, topic, id].compact.map(&:to_s)
|
27
|
+
File.join(*segments)
|
28
|
+
end
|
29
|
+
|
30
|
+
def configure_connection options
|
31
|
+
@connection = Connection.factory(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def http_connection
|
35
|
+
@connection ||= Connection.factory
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -1,49 +1,13 @@
|
|
1
|
-
|
1
|
+
module Vayacondios::Client
|
2
2
|
class HttpClient
|
3
|
-
include
|
3
|
+
include HttpRead, HttpWrite, HttpAdmin
|
4
4
|
|
5
|
-
|
6
|
-
field :port, String, :default => '8000'
|
7
|
-
field :organization, String, :default => 'infochimps'
|
8
|
-
|
9
|
-
class Error < StandardError; end
|
5
|
+
attr_reader :organization
|
10
6
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
uri_str = "http://#{host}:#{port}/v1"
|
15
|
-
uri_str += "/#{organization}" if organization
|
16
|
-
@uri ||= URI(uri_str)
|
17
|
-
end
|
18
|
-
|
19
|
-
def fetch(type, id)
|
20
|
-
request(:get, type, id)
|
21
|
-
end
|
22
|
-
|
23
|
-
def insert(document = {}, type = nil, id = nil)
|
24
|
-
id ||= document.delete(:_id) || document.delete('_id')
|
25
|
-
type ||= document.delete(:_type) || document.delete('_type')
|
26
|
-
|
27
|
-
request(:post, type, id, MultiJson.dump(document))
|
7
|
+
def initialize(options = {})
|
8
|
+
@organization = options.delete(:organization)
|
9
|
+
configure_connection(options) unless options.empty?
|
28
10
|
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def request(method, type, id = nil, document = nil)
|
33
|
-
path = File.join(uri.path, type.to_s, *id.to_s.split(/\W/))
|
34
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
35
11
|
|
36
|
-
params = [method.to_sym, path]
|
37
|
-
params += [document, {'Content-Type' => 'application/json'}] unless document.nil?
|
38
|
-
|
39
|
-
response = http.send *params
|
40
|
-
|
41
|
-
if Net::HTTPSuccess === response
|
42
|
-
MultiJson.load(response.body) rescue response.body
|
43
|
-
else
|
44
|
-
raise Error.new("Error (#{response.code}) while #{method.to_s == 'get' ? 'fetching' : 'inserting'} document: " + response.body)
|
45
|
-
end
|
46
|
-
end
|
47
12
|
end
|
48
13
|
end
|
49
|
-
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Vayacondios::Client
|
2
|
+
module HttpRead
|
3
|
+
include Connection
|
4
|
+
|
5
|
+
# Retrieve one stash
|
6
|
+
def get(topic, id = nil)
|
7
|
+
http_connection.get url('stash', topic, id)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Search stashes
|
11
|
+
def get_many(query = {})
|
12
|
+
http_connection.get url('stashes') do |req|
|
13
|
+
req.body = query
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Search events
|
18
|
+
def events(topic, query = {})
|
19
|
+
http_connection.get url('events', topic) do |req|
|
20
|
+
req.body = query
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Stream events
|
25
|
+
# only works with net/http
|
26
|
+
def stream(topic, query = {}, &on_event)
|
27
|
+
uri = http_connection.url_prefix
|
28
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
29
|
+
path = File.join(uri.request_uri, url('stream', topic))
|
30
|
+
request = Net::HTTP::Get.new(path)
|
31
|
+
http.request(request) do |response|
|
32
|
+
buffer = ''
|
33
|
+
response.read_body do |chunk|
|
34
|
+
buffer += chunk
|
35
|
+
while line = buffer.slice!(/^[^\n].*\n/)
|
36
|
+
on_event.call MultiJson.load(line.strip)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module HttpWrite
|
45
|
+
include Connection
|
46
|
+
|
47
|
+
# Create a stash
|
48
|
+
def set(topic, id = nil, stash = {})
|
49
|
+
http_connection.post url('stash', topic, id) do |req|
|
50
|
+
req.body = stash
|
51
|
+
end
|
52
|
+
end
|
53
|
+
alias :set! :set
|
54
|
+
|
55
|
+
# Create an event
|
56
|
+
def announce(topic, event = {}, id = nil)
|
57
|
+
http_connection.post url('event', topic, id) do |req|
|
58
|
+
req.body = event
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
module HttpAdmin
|
64
|
+
include Connection
|
65
|
+
|
66
|
+
# Delete one stash
|
67
|
+
def unset(topic, id = nil)
|
68
|
+
http_connection.delete url('stash', topic, id)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Delete stashes by search
|
72
|
+
def unset_many(query = {})
|
73
|
+
http_connection.delete url('stashes') do |req|
|
74
|
+
req.body = query
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Delete all events by topic and query
|
79
|
+
def clear_events(topic, query = {})
|
80
|
+
http_connection.delete url('events', topic) do |req|
|
81
|
+
req.body = query
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'configliere'
|
2
|
+
require 'faraday'
|
3
|
+
require 'faraday_middleware'
|
4
|
+
require 'gorillib'
|
5
|
+
require 'gorillib/metaprogramming/class_attribute'
|
6
|
+
require 'gorillib/string/inflections'
|
7
|
+
require 'logger'
|
8
|
+
require 'multi_json'
|
9
|
+
|
10
|
+
require 'vayacondios'
|
11
|
+
require 'vayacondios/configuration'
|
12
|
+
require 'vayacondios/client/configuration'
|
13
|
+
require 'vayacondios/client/connection'
|
14
|
+
require 'vayacondios/client/http_methods'
|
15
|
+
require 'vayacondios/client/http_client'
|
16
|
+
|
17
|
+
module Vayacondios
|
18
|
+
module Client
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|