orchestrate 0.5.1 → 0.6.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.
@@ -3,359 +3,346 @@ require 'faraday_middleware'
3
3
 
4
4
  module Orchestrate
5
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
6
  class Client
12
7
 
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
8
+ # @return [String] The API key provided
9
+ attr_reader :api_key
10
+
11
+ # @return [Faraday::Connection] The Faraday HTTP connection.
12
+ attr_reader :http
13
+
14
+ # @return [Proc] The block used to configure faraday.
15
+ attr_reader :faraday_configuration
16
+
17
+ # Instantiate a new Client for an Orchestrate application.
18
+ # @param api_key [#to_s] The API Key for your Orchestrate application.
19
+ # @yieldparam [Faraday::Connection] The setup for the faraday connection.
20
+ # @return Orchestrate::Client
21
+ # @todo api_key -> app_url, parse api_key from auth section of url
22
+ def initialize(api_key, &block)
23
+ @api_key = api_key
24
+ @faraday_configuration = block
25
+ @http = Faraday.new("https://api.orchestrate.io") do |faraday|
26
+ block = lambda{|f| f.adapter Faraday.default_adapter } unless block
27
+ block.call faraday
33
28
 
34
29
  # faraday seems to want you do specify these twice.
35
- faraday.request :basic_auth, config.api_key, ''
36
- faraday.basic_auth config.api_key, ''
30
+ faraday.request :basic_auth, api_key, ''
31
+ faraday.basic_auth api_key, ''
37
32
 
38
33
  # parses JSON responses
39
34
  faraday.response :json, :content_type => /\bjson$/
40
35
  end
41
36
  end
42
37
 
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 }
38
+ # Tests authentication with Orchestrate.
39
+ # @return Orchestrate::API::Response
40
+ # @raise Orchestrate::API::Unauthorized if the client could not authenticate.
41
+ def ping
42
+ send_request :get, []
65
43
  end
66
44
 
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})}
45
+ # @!group Collections
46
+
47
+ # [Search the items in
48
+ # a collection](http://orchestrate.io/docs/api/#search) using a Lucene
49
+ # Query Syntax.
50
+ # @param collection [#to_s] The name of the collection
51
+ # @param query [String] The [Lucene Query String][lucene] to query the collection with.
52
+ # [lucene]: http://lucene.apache.org/core/4_3_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Overview
53
+ # @param options [Hash] Parameters for the query
54
+ # @option options [Integer] :limit (10) The number of results to return. Maximum 100.
55
+ # @option options [Integer] :offset (0) The starting position of the results.
56
+ # @return Orchestrate::API::CollectionResponse
57
+ # @raise Orchestrate::API::InvalidSearchParam The :limit/:offset values are not valid.
58
+ # @raise Orchestrate::API::SearchQueryMalformed if query isn't a valid Lucene query.
59
+ def search(collection, query, options={})
60
+ send_request :get, [collection], { query: options.merge({query: query}),
61
+ response: API::CollectionResponse }
80
62
  end
81
63
 
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
- #
64
+ # [Deletes an entire collection](http://orchestrate.io/docs/api/#collections/delete)
65
+ # @param collection [#to_s] The name of the collection
66
+ # @return Orchestrate::API::Response
67
+ # @note The Orchestrate API will return succesfully regardless of if the collection exists or not.
89
68
  def delete_collection(collection)
90
69
  send_request :delete, [collection], { query: {force:true} }
91
70
  end
92
71
 
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
- #
72
+ # @!endgroup
73
+ # @!group Key/Value
74
+
75
+ # [Retrieves the value assigned to a key](http://orchestrate.io/docs/api/#key/value/get).
76
+ # If a `ref` parameter is provided, [retrieves a historical value for the
77
+ # key](http://orchestrate.io/docs/api/#refs/get14)
78
+ # @param collection [#to_s] The name of the collection.
79
+ # @param key [#to_s] The name of the key.
80
+ # @param ref [#to_s] The opaque version identifier of the ref to return, if a historical value is desired.
81
+ # @return Orchestrate::API::ItemResponse
82
+ # @raise Orchestrate::API::NotFound If the key or ref doesn't exist.
83
+ # @raise Orchestrate::API::MalformedRef If the ref provided is not a valid ref.
106
84
  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
85
+ path = [collection, key]
86
+ path.concat(['refs', ref]) if ref
87
+ send_request :get, path, { response: API::ItemResponse }
112
88
  end
113
89
 
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
- #
90
+ # [List historical refs for values of a key](http://orchestrate.io/docs/api/#refs/list15).
91
+ # Results are time-ordered newest-to-oldest and paginated.
92
+ # @param collection [#to_s] The name of the collection.
93
+ # @param key [#to_s] The name of the key.
94
+ # @param options [Hash] Parameters for the query.
95
+ # @option options [Integer] :limit (10) The number of results to return. Maximum 100.
96
+ # @option options [Integer] :offset (0) The starting position of the results.
97
+ # @option options [true, false] :values (false) Whether to return the value
98
+ # for each ref. Refs with no content (for example, deleted with `#delete`) will not have
99
+ # a value, but marked with a `'tombstone' => true` key.
100
+ # @return Orchestrate::API::CollectionResponse
101
+ # @raise Orchestrate::API::NotFound If there are no values for the provided key/collection.
102
+ # @raise Orchestrate::API::InvalidSearchParam The :limit/:offset values are not valid.
103
+ # @raise Orchestrate::API::MalformedRef If the ref provided is not a valid ref.
104
+ def list_refs(collection, key, options={})
105
+ send_request :get, [collection, key, :refs], { query: options, response: API::CollectionResponse }
106
+ end
107
+
108
+ # [Updates the value associated with
109
+ # a key](http://orchestrate.io/docs/api/#key/value/put-\(create/update\)).
110
+ # If the key does not currently have a value, will create the value.
111
+ # @param collection [#to_s] The name of the collection.
112
+ # @param key [#to_s] The name of the key.
113
+ # @param body [#to_json] The value for the key.
114
+ # @param condition [String, false, nil] Conditions for setting the value.
115
+ # If `String`, value used as `If-Match`, value will only be updated if key's current value's ref matches.
116
+ # If `false`, uses `If-None-Match` the value will only be set if there is no existent value for the key.
117
+ # If `nil` (default), value is set regardless.
118
+ # @return Orchestrate::API::ItemResponse
119
+ # @raise Orchestrate::API::BadRequest the body is not valid JSON.
120
+ # @raise Orchestrate::API::IndexingConflict One of the value's keys
121
+ # contains a value of a different type than the schema that exists for
122
+ # the collection.
123
+ # @see Orchestrate::API::IndexingConflict
124
+ # @raise Orchestrate::API::VersionMismatch A ref was provided, but it does not match the ref for the current value.
125
+ # @raise Orchestrate::API::AlreadyPresent the `false` condition was given, but a value already exists for this collection/key combo.
128
126
  def put(collection, key, body, condition=nil)
129
127
  headers={}
130
128
  if condition.is_a?(String)
131
- headers['If-Match'] = format_ref(condition)
129
+ headers['If-Match'] = API::Helpers.format_ref(condition)
132
130
  elsif condition == false
133
131
  headers['If-None-Match'] = '*'
134
132
  end
135
- send_request :put, [collection, key], { body: body, headers: headers }
133
+ send_request :put, [collection, key], { body: body, headers: headers, response: API::ItemResponse }
136
134
  end
137
135
  alias :put_if_unmodified :put
138
136
 
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
- #
137
+ # [Creates the value for a key if none
138
+ # exists](http://orchestrate.io/docs/api/#key/value/put-\(create/update\)).
139
+ # @see #put
144
140
  def put_if_absent(collection, key, body)
145
141
  put collection, key, body, false
146
142
  end
147
143
 
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
- #
144
+ # [Sets the current value of a key to a null
145
+ # object](http://orchestrate.io/docs/api/#key/value/delete11).
146
+ # @param collection [#to_s] The name of the collection.
147
+ # @param key [#to_s] The name of the key.
148
+ # @param ref [#to_s] If specified, deletes the ref only if the current value's ref matches.
149
+ # @return Orchestrate::API::Response
150
+ # @raise Orchestrate::API::VersionMismatch if the provided ref is not the ref for the current value.
151
+ # @note previous versions of the values at this key are still available via #list_refs and #get.
158
152
  def delete(collection, key, ref=nil)
159
153
  headers = {}
160
- headers['If-Match'] = format_ref(ref) if ref
154
+ headers['If-Match'] = API::Helpers.format_ref(ref) if ref
161
155
  send_request :delete, [collection, key], { headers: headers }
162
156
  end
163
157
 
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
- #
158
+ # [Purges the current value and ref history associated with the
159
+ # key](http://orchestrate.io/docs/api/#key/value/delete11).
160
+ # @param collection [#to_s] The name of the collection.
161
+ # @param key [#to_s] The name of the key.
162
+ # @return Orchestrate::API::Response
163
+ # @todo take an optional ref for If-Match
172
164
  def purge(collection, key)
173
165
  send_request :delete, [collection, key], { query: { purge: true } }
174
166
  end
175
167
 
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
- #
168
+ # [List the KeyValue items in a collection](http://orchestrate.io/docs/api/#key/value/list).
169
+ # Results are sorted results lexicographically by key name and paginated.
170
+ # @param collection [#to_s] The name of the collection
171
+ # @param options [Hash] Parameters for the query
172
+ # @option options [Integer] :limit (10) The number of results to return. Maximum 100.
173
+ # @option options [String] :start The inclusive start key of the query range.
174
+ # @option options [String] :after The exclusive start key of the query range.
175
+ # @option options [String] :before The exclusive end key of the query range.
176
+ # @option options [String] :end The inclusive end key of the query range.
177
+ # @note The Orchestrate API may return an error if you include both the
178
+ # :start/:after or :before/:end keys. The client will not stop you from doing this.
179
+ # @note To include all keys in a collection, do not include any :start/:after/:before/:end parameters.
180
+ # @return Orchestrate::API::CollectionResponse
181
+ # @raise Orchestrate::API::InvalidSearchParam The :limit value is not valid.
182
+ def list(collection, options={})
183
+ API::Helpers.range_keys!('key', options)
184
+ send_request :get, [collection], { query: options, response: API::CollectionResponse }
185
+ end
186
+
187
+ # @!endgroup
188
+ # @!group Events
189
+
190
+ # [Retrieves an individual event](http://orchestrate.io/docs/api/#events/get19).
191
+ # @param collection [#to_s] The name of the collection.
192
+ # @param key [#to_s] The name of the key.
193
+ # @param event_type [#to_s] The category for the event.
194
+ # @param timestamp [Time, Date, Integer, String] The timestamp for the event.
195
+ # If a String, must match the [Timestamp specification](http://orchestrate.io/docs/api/#events/timestamps)
196
+ # and include the milliseconds portion.
197
+ # @param ordinal [Integer, #to_s] The ordinal for the event in the given timestamp.
198
+ # @return Orchestrate::API::ItemResponse
199
+ # @raise Orchestrate::API::NotFound If the requested event doesn't exist.
193
200
  def get_event(collection, key, event_type, timestamp, ordinal)
194
- send_request :get, [collection, key, 'events', event_type, timestamp, ordinal]
201
+ timestamp = API::Helpers.timestamp(timestamp)
202
+ path = [collection, key, 'events', event_type, timestamp, ordinal]
203
+ send_request :get, path, { response: API::ItemResponse }
195
204
  end
196
205
 
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
- #
206
+ # [Creates an event](http://orchestrate.io/docs/api/#events/post-\(create\)).
207
+ # @param collection [#to_s] The name of the collection.
208
+ # @param key [#to_s] The name of the key.
209
+ # @param event_type [#to_s] The category for the event.
210
+ # @param body [#to_json] The value for the event.
211
+ # @param timestamp [Time, Date, Integer, String, nil] The timestamp for the event.
212
+ # If omitted, the timestamp is created by Orchestrate.
213
+ # If a String, must match the [Timestamp specification](http://orchestrate.io/docs/api/#events/timestamps).
214
+ # @return Orchestrate::API::ItemResponse
215
+ # @raise Orchestrate::API::BadRequest If the body is not valid JSON.
213
216
  def post_event(collection, key, event_type, body, timestamp=nil)
217
+ timestamp = API::Helpers.timestamp(timestamp)
214
218
  path = [collection, key, 'events', event_type, timestamp].compact
215
- send_request :post, path, { body: body }
219
+ send_request :post, path, { body: body, response: API::ItemResponse }
216
220
  end
217
221
 
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
- #
222
+ # [Updates an existing event](http://orchestrate.io/docs/api/#events/put-\(update\)).
223
+ # @param collection [#to_s] The name of the collection.
224
+ # @param key [#to_s] The name of the key.
225
+ # @param event_type [#to_s] The category for the event.
226
+ # @param timestamp [Time, Date, Integer, String, nil] The timestamp for the event.
227
+ # If a String, must match the [Timestamp specification](http://orchestrate.io/docs/api/#events/timestamps)
228
+ # and include the milliseconds portion.
229
+ # @param ordinal [Integer, #to_s] The ordinal for the event in the given timestamp.
230
+ # @param body [#to_json] The value for the event.
231
+ # @param ref [nil, #to_s] If provided, used as `If-Match`, and event will
232
+ # only update if its current value has the provided ref. If omitted,
233
+ # updates the event regardless.
234
+ # @return Orchestrate::API::ItemResponse
235
+ # @raise Orchestrate::API::NotFound The specified event doesn't exist.
236
+ # @raise Orchestrate::API::BadRequest If the body is not valid JSON.
237
+ # @raise Orchestrate::API::VersionMismatch The event's current value has a ref that does not match the provided ref.
238
+ # @raise Orchestrate::API::MalformedRef If the ref provided is not a valid ref.
237
239
  def put_event(collection, key, event_type, timestamp, ordinal, body, ref=nil)
240
+ timestamp = API::Helpers.timestamp(timestamp)
238
241
  path = [collection, key, 'events', event_type, timestamp, ordinal]
239
242
  headers = {}
240
- headers['If-Match'] = format_ref(ref) if ref
241
- send_request :put, path, { body: body, headers: headers }
243
+ headers['If-Match'] = API::Helpers.format_ref(ref) if ref
244
+ send_request :put, path, { body: body, headers: headers, response: API::ItemResponse }
242
245
  end
243
246
 
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
- #
247
+ # [Deletes an existing event](http://orchestrate.io/docs/api/#events/delete22).
248
+ # @param collection [#to_s] The name of the collection.
249
+ # @param key [#to_s] The name of the key.
250
+ # @param event_type [#to_s] The category for the event.
251
+ # @param timestamp [Time, Date, Integer, String, nil] The timestamp for the event.
252
+ # If a String, must match the [Timestamp specification](http://orchestrate.io/docs/api/#events/timestamps)
253
+ # and include the milliseconds portion.
254
+ # @param ordinal [Integer, #to_s] The ordinal for the event in the given timestamp.
255
+ # @param ref [nil, #to_s] If provided, used as `If-Match`, and event will
256
+ # only update if its current value has the provided ref. If omitted,
257
+ # updates the event regardless.
258
+ # @return Orchestrate::API::Response
259
+ # @raise Orchestrate::API::VersionMismatch The event's current value has a ref that does not match the provided ref.
260
+ # @raise Orchestrate::API::MalformedRef If the ref provided is not a valid ref.
262
261
  def purge_event(collection, key, event_type, timestamp, ordinal, ref=nil)
262
+ timestamp = API::Helpers.timestamp(timestamp)
263
263
  path = [collection, key, 'events', event_type, timestamp, ordinal]
264
264
  headers = {}
265
- headers['If-Match'] = format_ref(ref) if ref
265
+ headers['If-Match'] = API::Helpers.format_ref(ref) if ref
266
266
  send_request :delete, path, { query: { purge: true }, headers: headers }
267
267
  end
268
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 }
269
+ # [Lists events associated with a key](http://orchestrate.io/docs/api/#events/list23).
270
+ # Results are time-ordered in reverse-chronological order, and paginated.
271
+ # @param collection [#to_s] The name of the collection.
272
+ # @param key [#to_s] The name of the key.
273
+ # @param event_type [#to_s] The category for the event.
274
+ # @param options [Hash] The parameters for the query.
275
+ # @option options [Integer] :limit (10) The number of results to return. Default 100.
276
+ # @option options [Date, Time, String, Integer] :start The inclusive start of the range.
277
+ # @option options [Date, Time, String, Integer] :after The exclusive start of the range.
278
+ # @option options [Date, Time, String, Integer] :before The exclusive end of the range.
279
+ # @option options [Date, Time, String, Integer] :end The inclusive end of the range.
280
+ # @return Orchestrate::API::CollectionResponse
281
+ # @raise Orchestrate::API::NotFound If the provided collection/key doesn't exist.
282
+ def list_events(collection, key, event_type, options={})
283
+ (options.keys & [:start, :after, :before, :end]).each do |param|
284
+ options[param] = API::Helpers.timestamp(options[param])
285
+ end
286
+ API::Helpers.range_keys!('event', options)
287
+ path = [collection, key, 'events', event_type]
288
+ send_request :get, path, { query: options, response: API::CollectionResponse }
295
289
  end
296
290
 
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
- #
291
+ # @!endgroup
292
+ # @!group Graphs / Relations
293
+
294
+ # [Retrieves a relation's associated objects](http://orchestrate.io/docs/api/#graph/get26).
295
+ # @param collection [#to_s] The name of the collection.
296
+ # @param key [#to_s] The name of the key.
297
+ # @param kinds [#to_s] The relationship kinds and depth to query.
298
+ # @example Retrieves the friend's of John's family
299
+ # client.get_relations(:users, 'john', :family, :friends)
300
+ # @example If the relation facets exist in an array, use:
301
+ # relations = [:family, :friends]
302
+ # client.get_relations(:users, 'john', *relations)
303
+ # @return Orchestrate::API::CollectionResponse
304
+ # @raise Orchestrate::API::NotFound If the given collection/key doesn't exist.
309
305
  def get_relations(collection, key, *kinds)
310
306
  path = [collection, key, 'relations'].concat(kinds)
311
- send_request :get, path
307
+ send_request :get, path, {response: API::CollectionResponse}
312
308
  end
313
309
 
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
- #
310
+ # [Creates a relationship between two Key/Value objects](http://orchestrate.io/docs/api/#graph/put).
311
+ # Relations can span collections.
312
+ # @param collection [#to_s] The name of the collection.
313
+ # @param key [#to_s] The name of the key.
314
+ # @param kind [#to_s] The kind of relationship to create.
315
+ # @param to_collection [#to_s] The name of the collection to relate to.
316
+ # @param to_key [#to_s] The name of the key to relate to.
317
+ # @return Orchestrate::API::Response
318
+ # @raise Orchestrate::API::NotFound If either end of the relation doesn't exist.
325
319
  def put_relation(collection, key, kind, to_collection, to_key)
326
320
  send_request :put, [collection, key, 'relation', kind, to_collection, to_key]
327
321
  end
328
322
 
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
- #
323
+ # [Deletes a relationship between two objects](http://orchestrate.io/docs/api/#graph/delete28).
324
+ # @param collection [#to_s] The name of the collection.
325
+ # @param key [#to_s] The name of the key.
326
+ # @param kind [#to_s] The kind of relationship to delete.
327
+ # @param to_collection [#to_s] The name of the collection to relate to.
328
+ # @param to_key [#to_s] The name of the key to relate to.
329
+ # @return Orchestrate::API::Response
340
330
  def delete_relation(collection, key, kind, to_collection, to_key)
341
331
  path = [collection, key, 'relation', kind, to_collection, to_key]
342
332
  send_request :delete, path, { query: {purge: true} }
343
333
  end
344
334
 
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:
335
+ # @!endgroup
336
+
337
+ # Performs requests in parallel. Requires using a Faraday adapter that supports parallel requests.
338
+ # @yieldparam accumulator [Hash] A place to store the results of the parallel responses.
339
+ # @example Performing three requests at once
353
340
  # responses = client.in_parallel do |r|
354
341
  # r[:some_items] = client.list(:site_globals)
355
342
  # r[:user] = client.get(:users, current_user_key)
356
343
  # r[:user_feed] = client.list_events(:users, current_user_key, :notices)
357
344
  # end
358
- #
345
+ # @see README See the Readme for more examples.
359
346
  def in_parallel(&block)
360
347
  accumulator = {}
361
348
  http.in_parallel do
@@ -364,47 +351,48 @@ module Orchestrate
364
351
  accumulator
365
352
  end
366
353
 
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, {})
354
+ # Performs an HTTP request against the Orchestrate API
355
+ # @param method [Symbol] The HTTP method - :get, :post, :put, :delete
356
+ # @param path [Array<#to_s>] Path segments for the request's URI. Prepended by 'v0'.
357
+ # @param options [Hash] extra parameters
358
+ # @option options [Hash] :query ({}) Query String parameters.
359
+ # @option options [#to_json] :body ('') The request body.
360
+ # @option options [Hash] :headers ({}) Extra request headers.
361
+ # @option options [Class] :response (Orchestrate::API::Response) A subclass of Orchestrate::API::Response to instantiate and return
362
+ # @return API::Response
363
+ # @raise [Orchestrate::API::RequestError, Orchestrate::API::ServiceError] see http://orchestrate.io/docs/api/#errors
364
+ def send_request(method, path, options={})
365
+ path = ['/v0'].concat(path).join('/')
366
+ query_string = options.fetch(:query, {})
367
+ body = options.fetch(:body, '')
368
+ headers = options.fetch(:headers, {})
384
369
  headers['User-Agent'] = "ruby/orchestrate/#{Orchestrate::VERSION}"
385
370
  headers['Accept'] = 'application/json' if method == :get
386
371
 
387
- http.send(method) do |request|
388
- config.logger.debug "Performing #{method.to_s.upcase} request to \"#{url}\""
389
- request.url url, query_string
372
+ response = http.send(method) do |request|
373
+ request.url path, query_string
390
374
  if [:put, :post].include?(method)
391
375
  headers['Content-Type'] = 'application/json'
392
376
  request.body = body.to_json
393
377
  end
394
378
  headers.each {|header, value| request[header] = value }
395
379
  end
396
- end
397
-
398
- # ------------------------------------------------------------------------
399
380
 
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(/"/,'')}\""
381
+ if ! response.success?
382
+ err_type = API::ERRORS.find do |err|
383
+ err.status == response.status && err.code == response.body['code']
384
+ end
385
+ if err_type
386
+ raise err_type.new(response)
387
+ elsif response.status >= 500
388
+ raise API::ServiceError.new(response)
389
+ elsif response.status >= 400
390
+ raise API::RequestError.new(response)
391
+ end
406
392
  end
407
-
393
+ response_class = options.fetch(:response, API::Response)
394
+ response_class.new(response, self)
395
+ end
408
396
  end
409
397
 
410
398
  end