orchestrate 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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