orchestrate 0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d745f6432de35d38a03090bad7e072c5050f2660
4
+ data.tar.gz: b48814208a0b00a4c4d80bad1ea1e0a7d9c3e51c
5
+ SHA512:
6
+ metadata.gz: 99d1b6e4fffed62524458cd0862fc71d0d6f3d6cdc2187ce8882007159bbddfda38cac003f304ffb53e75fa8fceda1a8fe389963c6ff50448db84f796c112e16
7
+ data.tar.gz: c5f59656dc41f5b4eab6f9840a838b69938e20ac50d9d3c8cf44d0e337a946de98e4290260dd2e64b19c43b56351a86e0dc59515035f6554f2673e2d555a9419
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.lock
3
+ *.rbc
4
+ *.log
5
+ .bundle
6
+ .config
7
+ coverage
8
+ InstalledFiles
9
+ lib/bundler/man
10
+ pkg
11
+ rdoc
12
+ spec/reports
13
+ test/tmp
14
+ test/version_tmp
15
+ tmp
16
+
17
+ # YARD artifacts
18
+ .yardoc
19
+ _yardoc
20
+ doc/
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - 2.1.0
5
+ - 2.0.0
6
+ - 1.9.3
7
+ before_install: gem install bundler -v 1.6.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in orchestrate-rails.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 jimcar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,115 @@
1
+ Orchestrate API for Ruby
2
+ ========================
3
+ [![Build Status](https://travis-ci.org/orchestrate-io/orchestrate-ruby.png?branch=master)](https://travis-ci.org/orchestrate-io/orchestrate-ruby)
4
+
5
+ Ruby client interface for the [Orchestrate.io](http://orchestrate.io) REST API.
6
+
7
+ [rDoc Documentation](http://rdoc.info/github/orchestrate-io/orchestrate-ruby/master/frames)
8
+
9
+ ## Getting Started
10
+
11
+ Provide your API key:
12
+
13
+ ``` ruby
14
+ Orchestrate::Configuration.api_key = '8ce391a...'
15
+ ```
16
+
17
+ Create a client:
18
+
19
+ ``` ruby
20
+ client = Orchestrate::Client.new
21
+ ```
22
+
23
+ and start making requests:
24
+
25
+ ``` ruby
26
+ client.list(:my_collection)
27
+ ```
28
+
29
+ ## Swapping out the HTTP backend
30
+
31
+ This gem uses [Faraday][] for its HTTP needs -- and Faraday allows you to change the underlying HTTP client used. It defaults to `Net::HTTP` but if you wanted to use [Typhoeus][] or [EventMachine HTTP][em-http], doing so would be easy. Alternate Faraday backends enable using callbacks or parallel request support.
32
+
33
+ In your Orchestrate configuration, simply provide a `faraday` key with a block that will be called with the `Faraday::Connection` object. You may decorate it with middleware or change the adapter as described in the Faraday README. Examples are below.
34
+
35
+ You may use Faraday's `test` adapter to stub out calls to the Orchestrate API in your tests. See `tests/test_helper.rb` and the tests in `tests/orchestrate/api/*_test.rb` for examples.
36
+
37
+ [Faraday]: https://github.com/lostisland/faraday/
38
+ [Typhoeus]: https://github.com/typhoeus/typhoeus#readme
39
+ [em-http]: https://github.com/igrigorik/em-http-request#readme
40
+
41
+ ### Parallel HTTP requests
42
+
43
+ If you're using a Faraday backend that enables parallelization, such as Typhoeus, EM-HTTP-Request, or EM-Synchrony you can use `Orchestrate::Client#in_parallel` to fire off multiple requests at once. If your Faraday backend does not support this, the method will still work as expected, but Faraday will output a warning to STDERR and the requests will be performed in series.
44
+
45
+ ``` ruby
46
+ client = Orchestrate::Client.new
47
+
48
+ responses = client.in_parallel do |r|
49
+ r[:list] = client.list(:my_collection)
50
+ r[:user] = client.get(:users, current_user_id)
51
+ r[:user_events] = client.list_events(:users, current_user_id, :notices)
52
+ end
53
+ # will return when all requests have completed
54
+
55
+ responses[:user] = #<Faraday::Response:0x00...>
56
+ ```
57
+
58
+ ### Callback Support
59
+
60
+ If you're using a Faraday backend that enables callbacks, such as EM-HTTP-Request or EM-Synchrony, you may use the callback interface to designate actions to perform when the request completes.
61
+
62
+ ``` ruby
63
+ client = Orchestrate::Client.new
64
+ response = client.list(:my_collection)
65
+ response.finished? # false
66
+ response.on_complete do
67
+ # do stuff with the response as normal
68
+ end
69
+ ```
70
+
71
+ If the Faraday backend adapter does not support callbacks, the block provided will be executed when `Orchestrate::Client#on_complete` is called.
72
+
73
+
74
+ ### Using with Typhoeus
75
+
76
+ Typhoeus is backed by libcurl and enables parallelization.
77
+
78
+ ``` ruby
79
+ require 'typhoeus'
80
+ require 'typhoeus/adapters/faraday'
81
+
82
+ Orchestrate.configure do |config|
83
+ config.faraday = {|f| f.adapter :typhoeus }
84
+ config.api_key = "my_api_key"
85
+ end
86
+ ```
87
+
88
+ ### Using with EM-HTTP-Request
89
+
90
+ EM-HTTP-Request is an HTTP client for Event Machine. It enables callback support and parallelization.
91
+
92
+
93
+ ``` ruby
94
+ require 'em-http-request'
95
+
96
+ Orchestrate.configure do |config|
97
+ config.faraday = {|f| f.adapter :em_http }
98
+ config.api_key = "my_api_key"
99
+ end
100
+ ```
101
+
102
+ ### Using with EM-Syncrony
103
+
104
+ EM-Synchrony is a collection of utility classes for EventMachine to help untangle evented code. It enables parallelization.
105
+
106
+ ``` ruby
107
+ require 'em-synchrony'
108
+
109
+ Orchestrate.configure do |config|
110
+ config.faraday = {|f| f.adapter :em_synchrony }
111
+ config.api_key = "my_api_key"
112
+ end
113
+ ```
114
+
115
+
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ require "rdoc/task"
4
+
5
+ task default: :test
6
+
7
+ Rake::TestTask.new do |test|
8
+ test.libs << "test"
9
+ test.test_files = FileList["test/**/*_test.rb"]
10
+ test.verbose = true
11
+ end
12
+
13
+ Rake::RDocTask.new(rdoc: "doc", clobber_rdoc: "doc:clean", rerdoc: "doc:force") do |rdoc|
14
+ rdoc.main = "README.md"
15
+ rdoc.title = "Orchestrate API Documentation"
16
+ rdoc.options << "--line-numbers"
17
+ rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
18
+ rdoc.rdoc_dir = "doc"
19
+ end
20
+
@@ -0,0 +1,46 @@
1
+ require "orchestrate/api"
2
+ require "orchestrate/client"
3
+ require "orchestrate/configuration"
4
+ require "orchestrate/helpers"
5
+ require "orchestrate/version"
6
+
7
+ #
8
+ # A library for supporting connections to the \Orchestrate API.
9
+ #
10
+ module Orchestrate
11
+
12
+ # Configuration ------------------------------------------------------------
13
+
14
+ class << self
15
+
16
+ #
17
+ # An instance of Configuration containing the current library
18
+ # configuration options.
19
+ #
20
+ attr_accessor :config
21
+
22
+ #
23
+ # Lazily initialize and return the Configuration instance.
24
+ #
25
+ def config # :nodoc:
26
+ @config ||= Configuration.new
27
+ end
28
+
29
+ #
30
+ # Configure the Orchestrate library. This method should be called during
31
+ # application or script initialization. At a bare minimum, the Orchestrate
32
+ # library will require that an API key is provided.
33
+ #
34
+ # ==== Example
35
+ #
36
+ # Orchestrate.configure do |config|
37
+ # config.api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
38
+ # end
39
+ #
40
+ def configure
41
+ yield config
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,15 @@
1
+ require "orchestrate"
2
+ require "orchestrate/api/extensions"
3
+
4
+ module Orchestrate
5
+
6
+ #
7
+ # Ruby gem +orchestrate-api+ provides an interface to the
8
+ # [Orchestrate](http://orchestrate.io) API.
9
+ #
10
+ # The Client class is used to setup the client and make HTTP requests.
11
+ #
12
+ module API
13
+ end
14
+
15
+ end
@@ -0,0 +1,57 @@
1
+ module Orchestrate::API
2
+
3
+ # Not used. But it does contain nice summary of orchestrate.io api errors.
4
+ #
5
+ module Error
6
+ def errors
7
+ @@errors ||= [
8
+ { :status => 400,
9
+ :code => :api_bad_request,
10
+ :desc => 'The API request is malformed.'
11
+ },
12
+ { :status => 500,
13
+ :code => :security_authentication,
14
+ :desc => 'An error occurred while trying to authenticate.'
15
+ },
16
+ { :status => 401,
17
+ :code => :security_unauthorized,
18
+ :desc => 'Valid credentials are required.'
19
+ },
20
+ { :status => 400,
21
+ :code => :search_param_invalid,
22
+ :desc => 'A provided search query param is invalid.'
23
+ },
24
+ { :status => 500,
25
+ :code => :search_index_not_found,
26
+ :desc => 'Index could not be queried for this application.'
27
+ },
28
+ { :status => 500,
29
+ :code => :internal_error,
30
+ :desc => 'Internal Error.'
31
+ },
32
+ { :status => 404,
33
+ :code => :items_not_found,
34
+ :desc => 'The requested items could not be found.'
35
+ },
36
+ { :status => 412,
37
+ :code => :item_version_mismatch,
38
+ :desc => 'The version of the item does not match.'
39
+ },
40
+ { :status => 412,
41
+ :code => :item_already_present,
42
+ :desc => 'The item is already present.'
43
+ },
44
+ { :status => 400,
45
+ :code => :item_ref_malformed,
46
+ :desc => 'The provided Item Ref is malformed.'
47
+ },
48
+ { :status => 409,
49
+ :code => :indexing_conflict,
50
+ :desc => 'The item has been stored but conflicts were detected ' +
51
+ 'when indexing. Conflicting fields have not been indexed.'
52
+ },
53
+ ]
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,14 @@
1
+ module Orchestrate::API
2
+
3
+ # Implement the blank? helpers here, instead of requiring active_support/core_ext.
4
+ class ::Object
5
+ def blank?
6
+ respond_to?(:empty?) ? empty? : !self
7
+ end
8
+
9
+ def present?
10
+ !blank?
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,410 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+
4
+ module Orchestrate
5
+
6
+ # ==== Ruby Client for the Orchestrate REST *API*.
7
+ #
8
+ # The primary entry point is the #send_request method, which generates a
9
+ # Request, and returns the Response to the caller.
10
+ #
11
+ class Client
12
+
13
+ # Orchestrate::Configuration instance for the client. If not explicitly
14
+ # provided during initialization, will default to Orchestrate.config
15
+ attr_accessor :config
16
+
17
+ # The Faraday HTTP "connection" for the client.
18
+ attr_accessor :http
19
+
20
+ # Initialize and return a new Client instance. Optionally, configure
21
+ # options for the instance by passing a Configuration object. If no
22
+ # custom configuration is provided, the configuration options from
23
+ # Orchestrate.config will be used.
24
+ def initialize(config = Orchestrate.config)
25
+ @config = config
26
+
27
+ @http = Faraday.new(config.base_url) do |faraday|
28
+ if config.faraday.respond_to?(:call)
29
+ config.faraday.call(faraday)
30
+ else
31
+ faraday.adapter Faraday.default_adapter
32
+ end
33
+
34
+ # faraday seems to want you do specify these twice.
35
+ faraday.request :basic_auth, config.api_key, ''
36
+ faraday.basic_auth config.api_key, ''
37
+
38
+ # parses JSON responses
39
+ faraday.response :json, :content_type => /\bjson$/
40
+ end
41
+ end
42
+
43
+ # -------------------------------------------------------------------------
44
+ # collection
45
+
46
+ # call-seq:
47
+ # client.list(collection_name, parameters = {}) -> response
48
+ #
49
+ # Performes a List query against the given collection. Results are sorted
50
+ # lexicographically by key name.
51
+ #
52
+ # +collection_name+:: A String or Symbol representing the name of the collection.
53
+ # +parameters+:: a Hash object containing query parameters:
54
+ # - +:limit+ - integer, number of results to return. Defaults to 10, Max 100.
55
+ # - +:start+ - string, start key of query range, including value.
56
+ # - +:after+ - string, start key of query range, excluding value.
57
+ # - +:before+ - string, end key of query range, excluding value.
58
+ # - +:end+ - string, end key of query range, including value.
59
+ #
60
+ # Note, you cannot provide *both* 'start' and 'after', or 'before' and 'end'
61
+ #
62
+ def list(collection, options={})
63
+ Orchestrate::Helpers.range_keys!('key', options)
64
+ send_request :get, [collection], { query: options }
65
+ end
66
+
67
+ # call-seq:
68
+ # client.list(collection_name, query, parameters = {}) -> response
69
+ #
70
+ # Performs a Search query against the given collection.
71
+ #
72
+ # +collection_name+:: a String or Symbol representing the name of the collection.
73
+ # +query+:: a String containing a Lucene query
74
+ # +parameters+:: a Hash object containing additional parameters:
75
+ # - +limit+: - integer, number of results to return. Defaults to 10, Max 100.
76
+ # - +offset+: - ingeger, the starting position of the results. Defaults to 0.
77
+ #
78
+ def search(collection, query, parameters={})
79
+ send_request :get, [collection], { query: parameters.merge({query: query})}
80
+ end
81
+
82
+ # call-seq:
83
+ # client.delete_collection(collection_name) -> response
84
+ #
85
+ # Deletes the given collection.
86
+ #
87
+ # +collection_name+:: a String or Symbol representing the name of the collection.
88
+ #
89
+ def delete_collection(collection)
90
+ send_request :delete, [collection], { query: {force:true} }
91
+ end
92
+
93
+ # -------------------------------------------------------------------------
94
+ # Key/Value
95
+
96
+ # call-seq:
97
+ # client.get(collection_name, key) -> response
98
+ # client.get(collection_name, key, ref) -> response
99
+ #
100
+ # Retreieves a value assigned to a key.
101
+ #
102
+ # +collection_name+:: a String or Symbol representing the name of the collection.
103
+ # +key+:: a String or Symbol representing the key for the value.
104
+ # +ref+:: if given, returns the value for the key at the specified ref. If omitted, returns the latest value for the key.
105
+ #
106
+ def get(collection, key, ref=nil)
107
+ if ref
108
+ send_request :get, [collection, key, 'refs', ref]
109
+ else
110
+ send_request :get, [collection, key]
111
+ end
112
+ end
113
+
114
+ # call-seq:
115
+ # client.put(collection_name, key, body) -> response
116
+ # client.put(collection_name, key, body, condition) -> response
117
+ #
118
+ # Creates or Updates the value at the specified key.
119
+ #
120
+ # +collection_name+:: a String or Symbol representing the name of the collection.
121
+ # +key+:: a String or Symbol representing the key for the value.
122
+ # +body+:: a Hash object representing the value for the key.
123
+ # +condition+::
124
+ # - +nil+ - the value for the specified key will be updated regardless.
125
+ # - String - used as 'If-Match'. The value will only be updated if the Key's current Value's Ref matches the given condition.
126
+ # - false - used as 'If-None-Match'. The value will only be created for the key if the key currently has no value.
127
+ #
128
+ def put(collection, key, body, condition=nil)
129
+ headers={}
130
+ if condition.is_a?(String)
131
+ headers['If-Match'] = format_ref(condition)
132
+ elsif condition == false
133
+ headers['If-None-Match'] = '*'
134
+ end
135
+ send_request :put, [collection, key], { body: body, headers: headers }
136
+ end
137
+ alias :put_if_unmodified :put
138
+
139
+ # call-seq:
140
+ # client.put_if_absent(collection_name, key, body) -> response
141
+ #
142
+ # Will create the value at the specified key only if there is no existing value for the specified key.
143
+ #
144
+ def put_if_absent(collection, key, body)
145
+ put collection, key, body, false
146
+ end
147
+
148
+ # call-seq:
149
+ # client.delete(collection_name, key) -> response
150
+ # client.delete(collection_name, key, ref) -> response
151
+ #
152
+ # Deletes the value of the specified key. Historical values for given refs are still available.
153
+ #
154
+ # +collection_name+:: a String or Symbol representing the name of the collection.
155
+ # +key+:: a String or Symbol representing the key for the value.
156
+ # +ref+:: if provided, used as 'If-Match', will only delete the key if the current value is the provided ref.
157
+ #
158
+ def delete(collection, key, ref=nil)
159
+ headers = {}
160
+ headers['If-Match'] = format_ref(ref) if ref
161
+ send_request :delete, [collection, key], { headers: headers }
162
+ end
163
+
164
+ # call-seq:
165
+ # client.purge(collection_name, key) -> response
166
+ #
167
+ # Deletes the value for the specified key as well as all historical values. Cannot be undone.
168
+ #
169
+ # +collection_name+:: a String or Symbol representing the name of the collection.
170
+ # +key+:: a String or Symbol representing the key for the value.
171
+ #
172
+ def purge(collection, key)
173
+ send_request :delete, [collection, key], { query: { purge: true } }
174
+ end
175
+
176
+ # -------------------------------------------------------------------------
177
+ # Events
178
+
179
+ # call-seq:
180
+ # client.get_event(collection_name, key, event_type, timestamp, ordinal) -> response
181
+ #
182
+ # Gets the event for the specified arguments.
183
+ #
184
+ # +collection_name+:: a String or Symbol representing the name of the collection.
185
+ # +key+:: a String or Symbol representing the key for the value.
186
+ # +event_type+:: a String or Symbol representing the category for the event.
187
+ # +timestamp+:: an Integer or String representing a time.
188
+ # - Integers are Milliseconds since Unix Epoch.
189
+ # - Strings must be formatted as per http://orchestrate.io/docs/api/#events/timestamps
190
+ # - A future version will support ruby Time objects.
191
+ # +ordinal+:: an Integer representing the order of the event for this timestamp.
192
+ #
193
+ def get_event(collection, key, event_type, timestamp, ordinal)
194
+ send_request :get, [collection, key, 'events', event_type, timestamp, ordinal]
195
+ end
196
+
197
+ # call-seq:
198
+ # client.post_event(collection_name, key, event_type) -> response
199
+ # client.post_event(collection_name, key, event_type, timestamp) -> response
200
+ #
201
+ # Creates an event.
202
+ #
203
+ # +collection_name+:: a String or Symbol representing the name of the collection.
204
+ # +key+:: a String or Symbol representing the key for the value.
205
+ # +event_type+:: a String or Symbol representing the category for the event.
206
+ # +body+:: a Hash object representing the value for the event.
207
+ # +timestamp+:: an Integer or String representing a time.
208
+ # - nil - Timestamp value will be created by Orchestrate.
209
+ # - Integers are Milliseconds since Unix Epoch.
210
+ # - Strings must be formatted as per http://orchestrate.io/docs/api/#events/timestamps
211
+ # - A future version will support ruby Time objects.
212
+ #
213
+ def post_event(collection, key, event_type, body, timestamp=nil)
214
+ path = [collection, key, 'events', event_type, timestamp].compact
215
+ send_request :post, path, { body: body }
216
+ end
217
+
218
+ # call-seq:
219
+ # client.put_event(collection_name, key, event_type, timestamp, ordinal, body) -> response
220
+ # client.put_event(collection_name, key, event_type, timestamp, ordinal, body, ref) -> response
221
+ #
222
+ # Puts the event for the specified arguments.
223
+ #
224
+ # +collection_name+:: a String or Symbol representing the name of the collection.
225
+ # +key+:: a String or Symbol representing the key for the value.
226
+ # +event_type+:: a String or Symbol representing the category for the event.
227
+ # +timestamp+:: an Integer or String representing a time.
228
+ # - Integers are Milliseconds since Unix Epoch.
229
+ # - Strings must be formatted as per http://orchestrate.io/docs/api/#events/timestamps
230
+ # - A future version will support ruby Time objects.
231
+ # +ordinal+:: an Integer representing the order of the event for this timestamp.
232
+ # +body+:: a Hash object representing the value for the event.
233
+ # +ref+::
234
+ # - +nil+ - The event will update regardless.
235
+ # - String - used as 'If-Match'. The event will only update if the event's current value matches this ref.
236
+ #
237
+ def put_event(collection, key, event_type, timestamp, ordinal, body, ref=nil)
238
+ path = [collection, key, 'events', event_type, timestamp, ordinal]
239
+ headers = {}
240
+ headers['If-Match'] = format_ref(ref) if ref
241
+ send_request :put, path, { body: body, headers: headers }
242
+ end
243
+
244
+ # call-seq:
245
+ # client.purge_event(collection, key, event_type, timestamp, ordinal) -> response
246
+ # client.purge_event(collection, key, event_type, timestamp, ordinal, ref) -> response
247
+ #
248
+ # Deletes the event for the specified arguments.
249
+ #
250
+ # +collection_name+:: a String or Symbol representing the name of the collection.
251
+ # +key+:: a String or Symbol representing the key for the value.
252
+ # +event_type+:: a String or Symbol representing the category for the event.
253
+ # +timestamp+:: an Integer or String representing a time.
254
+ # - Integers are Milliseconds since Unix Epoch.
255
+ # - Strings must be formatted as per http://orchestrate.io/docs/api/#events/timestamps
256
+ # - A future version will support ruby Time objects.
257
+ # +ordinal+:: an Integer representing the order of the event for this timestamp.
258
+ # +ref+::
259
+ # - +nil+ - The event will be deleted regardless.
260
+ # - String - used as 'If-Match'. The event will only be deleted if the event's current value matches this ref.
261
+ #
262
+ def purge_event(collection, key, event_type, timestamp, ordinal, ref=nil)
263
+ path = [collection, key, 'events', event_type, timestamp, ordinal]
264
+ headers = {}
265
+ headers['If-Match'] = format_ref(ref) if ref
266
+ send_request :delete, path, { query: { purge: true }, headers: headers }
267
+ end
268
+
269
+ # call-seq:
270
+ # client.list_events(collection_name, key, event_type) -> response
271
+ # client.list_events(collection_name, key, event_type, parameters = {}) -> response
272
+ #
273
+ # Puts the event for the specified arguments.
274
+ #
275
+ # +collection_name+:: a String or Symbol representing the name of the collection.
276
+ # +key+:: a String or Symbol representing the key for the value.
277
+ # +event_type+:: a String or Symbol representing the category for the event.
278
+ # +parameters+::
279
+ # - +:limit+ - integer, number of results to return. Defaults to 10, Max 100.
280
+ # - +:start+ - Integer/String representing the inclusive start to a range.
281
+ # - +:after+ - Integer/String representing the exclusive start to a range.
282
+ # - +:before+ - Integer/String representing the exclusive end to a range.
283
+ # - +:end+ - Integer/String representing the inclusive end to a range.
284
+ #
285
+ # Range parameters are formatted as ":timestamp/:ordinal":
286
+ # +timestamp+:: an Integer or String representing a time.
287
+ # - Integers are Milliseconds since Unix Epoch.
288
+ # - Strings must be formatted as per http://orchestrate.io/docs/api/#events/timestamps
289
+ # - A future version will support ruby Time objects.
290
+ # +ordinal+:: is optional.
291
+ #
292
+ def list_events(collection, key, event_type, parameters={})
293
+ Orchestrate::Helpers.range_keys!('event', parameters)
294
+ send_request :get, [collection, key, 'events', event_type], { query: parameters }
295
+ end
296
+
297
+ # -------------------------------------------------------------------------
298
+ # Graph
299
+
300
+ # call-seq:
301
+ # client.get_relations(collection_name, key, *kinds) -> response
302
+ #
303
+ # Returns the relation's collection, key and ref values.
304
+ #
305
+ # +collection_name+:: a String or Symbol representing the name of the collection.
306
+ # +key+:: a String or Symbol representing the key for the value.
307
+ # +kinds+:: one or more String or Symbol values representing the relations and depth to walk.
308
+ #
309
+ def get_relations(collection, key, *kinds)
310
+ path = [collection, key, 'relations'].concat(kinds)
311
+ send_request :get, path
312
+ end
313
+
314
+ # call-seq:
315
+ # client.put_relation(collection_name, key, kind, to_collection_name, to_key) -> response
316
+ #
317
+ # Stores a relationship between two Key/Value items. They do not need to be in the same collection.
318
+ #
319
+ # +collection_name+:: a String or Symbol representing the name of the collection.
320
+ # +key+:: a String or Symbol representing the key for the value.
321
+ # +kind+:: a String or Symbol value representing the relation type.
322
+ # +to_collection_name+:: a String or Symbol representing the name of the collection the related item belongs.
323
+ # +to_key+:: a String or Symbol representing the key for the related item.
324
+ #
325
+ def put_relation(collection, key, kind, to_collection, to_key)
326
+ send_request :put, [collection, key, 'relation', kind, to_collection, to_key]
327
+ end
328
+
329
+ # call-seq:
330
+ # client.delete_relation(collection_name, key, kind, to_collection, to_key) -> response
331
+ #
332
+ # Deletes a relationship between two Key/Value items.
333
+ #
334
+ # +collection_name+:: a String or Symbol representing the name of the collection.
335
+ # +key+:: a String or Symbol representing the key for the value.
336
+ # +kind+:: a String or Symbol value representing the relation type.
337
+ # +to_collection_name+:: a String or Symbol representing the name of the collection the related item belongs.
338
+ # +to_key+:: a String or Symbol representing the key for the related item.
339
+ #
340
+ def delete_relation(collection, key, kind, to_collection, to_key)
341
+ path = [collection, key, 'relation', kind, to_collection, to_key]
342
+ send_request :delete, path, { query: {purge: true} }
343
+ end
344
+
345
+ # call-seq:
346
+ # client.in_parallel {|responses| block } -> Hash
347
+ #
348
+ # Performs any requests generated inside the block in parallel. If the
349
+ # client isn't using a Faraday adapter that supports parallelization, will
350
+ # output a warning to STDERR.
351
+ #
352
+ # Example:
353
+ # responses = client.in_parallel do |r|
354
+ # r[:some_items] = client.list(:site_globals)
355
+ # r[:user] = client.get(:users, current_user_key)
356
+ # r[:user_feed] = client.list_events(:users, current_user_key, :notices)
357
+ # end
358
+ #
359
+ def in_parallel(&block)
360
+ accumulator = {}
361
+ http.in_parallel do
362
+ block.call(accumulator)
363
+ end
364
+ accumulator
365
+ end
366
+
367
+ # call-seq:
368
+ # client.send_request(method, url, opts={}) -> response
369
+ #
370
+ # Performs the HTTP request against the API and returns a Faraday::Response
371
+ #
372
+ # +method+ - the HTTP method, one of [ :get, :post, :put, :delete ]
373
+ # +url+ - an Array of segments to be joined with '/'
374
+ # +opts+
375
+ # - +:query+ - a Hash for the request query string
376
+ # - +:body+ - a Hash for the :put or :post request body
377
+ # - +:headers+ - a Hash the request headers
378
+ #
379
+ def send_request(method, url, opts={})
380
+ url = "/v0/#{url.join('/')}"
381
+ query_string = opts.fetch(:query, {})
382
+ body = opts.fetch(:body, '')
383
+ headers = opts.fetch(:headers, {})
384
+ headers['User-Agent'] = "ruby/orchestrate/#{Orchestrate::VERSION}"
385
+ headers['Accept'] = 'application/json' if method == :get
386
+
387
+ http.send(method) do |request|
388
+ config.logger.debug "Performing #{method.to_s.upcase} request to \"#{url}\""
389
+ request.url url, query_string
390
+ if [:put, :post].include?(method)
391
+ headers['Content-Type'] = 'application/json'
392
+ request.body = body.to_json
393
+ end
394
+ headers.each {|header, value| request[header] = value }
395
+ end
396
+ end
397
+
398
+ # ------------------------------------------------------------------------
399
+
400
+ private
401
+
402
+ # Formats the provided 'ref' to be quoted per API specification. If
403
+ # already quoted, does not add additional quotes.
404
+ def format_ref(ref)
405
+ "\"#{ref.gsub(/"/,'')}\""
406
+ end
407
+
408
+ end
409
+
410
+ end