Class: Orchestrate::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/orchestrate/client.rb

Overview

The "method client": A single entry point to an Orchestrate Application, with methods for accessing each API endpoint.

Instance Attribute Summary collapse

Collections collapse

Key/Value collapse

Events collapse

Graphs / Relations collapse

Instance Method Summary collapse

Constructor Details

#initialize(api_key, host = "https://api.orchestrate.io") {|The| ... } ⇒ Object

TODO:

api_key -> app_url, parse api_key from auth section of url

Instantiate a new Client for an Orchestrate application.

Parameters:

  • api_key (#to_s)

    The API Key for your Orchestrate application.

  • host (#to_s) (defaults to: "https://api.orchestrate.io")

    The host datacenter for your Orchestrate application.

Yield Parameters:

  • The (Faraday::Connection)

    setup for the faraday connection.



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/orchestrate/client.rb', line 28

def initialize(api_key, host="https://api.orchestrate.io", &block)
  @api_key = api_key
  @host = host
  @faraday_configuration = block
  @http = Faraday.new(@host) do |faraday|
    block = lambda{|f| f.adapter :net_http_persistent } unless block
    block.call faraday

    # faraday seems to want you do specify these twice.
    faraday.request :basic_auth, api_key, ''
    faraday.basic_auth api_key, ''
  end
end

Instance Attribute Details

#api_keyString (readonly)

Returns The API key provided.

Returns:

  • (String)

    The API key provided



11
12
13
# File 'lib/orchestrate/client.rb', line 11

def api_key
  @api_key
end

#faraday_configurationProc (readonly)

Returns The block used to configure faraday.

Returns:

  • (Proc)

    The block used to configure faraday.



20
21
22
# File 'lib/orchestrate/client.rb', line 20

def faraday_configuration
  @faraday_configuration
end

#hostString (readonly)

Returns The Orchestrate data center URL.

Returns:

  • (String)

    The Orchestrate data center URL



14
15
16
# File 'lib/orchestrate/client.rb', line 14

def host
  @host
end

#httpFaraday::Connection (readonly)

Returns The Faraday HTTP connection.

Returns:

  • (Faraday::Connection)

    The Faraday HTTP connection.



17
18
19
# File 'lib/orchestrate/client.rb', line 17

def http
  @http
end

Instance Method Details

#delete(collection, key, ref = nil) ⇒ Object

Note:

previous versions of the values at this key are still available via #list_refs and #get.

Sets the current value of a key to a null object.

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • ref (#to_s) (defaults to: nil)

    If specified, deletes the ref only if the current value's ref matches.

Returns:

  • Orchestrate::API::Response

Raises:

  • Orchestrate::API::VersionMismatch if the provided ref is not the ref for the current value.



217
218
219
220
221
# File 'lib/orchestrate/client.rb', line 217

def delete(collection, key, ref=nil)
  headers = {}
  headers['If-Match'] = API::Helpers.format_ref(ref) if ref
  send_request :delete, [collection, key], { headers: headers }
end

#delete_collection(collection) ⇒ Object

Note:

The Orchestrate API will return succesfully regardless of if the collection exists or not.

Deletes an entire collection

Parameters:

  • collection (#to_s)

    The name of the collection

Returns:

  • Orchestrate::API::Response



84
85
86
# File 'lib/orchestrate/client.rb', line 84

def delete_collection(collection)
  send_request :delete, [collection], { query: {force:true} }
end

#delete_relation(collection, key, kind, to_collection, to_key) ⇒ Object

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • kind (#to_s)

    The kind of relationship to delete.

  • to_collection (#to_s)

    The name of the collection to relate to.

  • to_key (#to_s)

    The name of the key to relate to.

Returns:

  • Orchestrate::API::Response



397
398
399
400
# File 'lib/orchestrate/client.rb', line 397

def delete_relation(collection, key, kind, to_collection, to_key)
  path = [collection, key, 'relation', kind, to_collection, to_key]
  send_request :delete, path, { query: {purge: true} }
end

#get(collection, key, ref = nil) ⇒ Object

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • ref (#to_s) (defaults to: nil)

    The opaque version identifier of the ref to return, if a historical value is desired.

Returns:

  • Orchestrate::API::ItemResponse

Raises:

  • Orchestrate::API::NotFound If the key or ref doesn't exist.

  • Orchestrate::API::MalformedRef If the ref provided is not a valid ref.



100
101
102
103
104
# File 'lib/orchestrate/client.rb', line 100

def get(collection, key, ref=nil)
  path = [collection, key]
  path.concat(['refs', ref]) if ref
  send_request :get, path, { response: API::ItemResponse }
end

#get_event(collection, key, event_type, timestamp, ordinal) ⇒ Object

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • event_type (#to_s)

    The category for the event.

  • timestamp (Time, Date, Integer, String)

    The timestamp for the event. If a String, must match the Timestamp specification and include the milliseconds portion.

  • ordinal (Integer, #to_s)

    The ordinal for the event in the given timestamp.

Returns:

  • Orchestrate::API::ItemResponse

Raises:

  • Orchestrate::API::NotFound If the requested event doesn't exist.



267
268
269
270
271
# File 'lib/orchestrate/client.rb', line 267

def get_event(collection, key, event_type, timestamp, ordinal)
  timestamp = API::Helpers.timestamp(timestamp)
  path = [collection, key, 'events', event_type, timestamp, ordinal]
  send_request :get, path, { response: API::ItemResponse }
end

#get_relations(collection, key, *kinds) ⇒ Object

Examples:

Retrieves the friend's of John's family

client.get_relations(:users, 'john', :family, :friends)

If the relation facets exist in an array, use:

relations = [:family, :friends]
client.get_relations(:users, 'john', *relations)

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • kinds (#to_s)

    The relationship kinds and depth to query.

Returns:

  • Orchestrate::API::CollectionResponse

Raises:

  • Orchestrate::API::NotFound If the given collection/key doesn't exist.



372
373
374
375
# File 'lib/orchestrate/client.rb', line 372

def get_relations(collection, key, *kinds)
  path = [collection, key, 'relations'].concat(kinds)
  send_request :get, path, {response: API::CollectionResponse}
end

#in_parallel {|accumulator| ... } ⇒ Object

Note:

This method is not Thread-safe. Requests generated from the same client in different threads while #in_parallel is running will behave unpredictably. Use #dup to create per-thread clients.

Performs requests in parallel. Requires using a Faraday adapter that supports parallel requests.

Examples:

Performing three requests at once

responses = client.in_parallel do |r|
  r[:some_items] = client.list(:site_globals)
  r[:user] = client.get(:users, current_user_key)
  r[:user_feed] = client.list_events(:users, current_user_key, :notices)
end

Yield Parameters:

  • accumulator (Hash)

    A place to store the results of the parallel responses.

See Also:

  • See the Readme for more examples.


416
417
418
419
420
421
422
# File 'lib/orchestrate/client.rb', line 416

def in_parallel(&block)
  accumulator = {}
  http.in_parallel do
    block.call(accumulator)
  end
  accumulator
end

#list(collection, options = {}) ⇒ Object

Note:

The Orchestrate API may return an error if you include both the :start/:after or :before/:end keys. The client will not stop you from doing this.

Note:

To include all keys in a collection, do not include any :start/:after/:before/:end parameters.

List the KeyValue items in a collection. Results are sorted results lexicographically by key name and paginated.

Parameters:

  • collection (#to_s)

    The name of the collection

  • options (Hash) (defaults to: {})

    Parameters for the query

Options Hash (options):

  • :limit (Integer) — default: 10

    The number of results to return. Maximum 100.

  • :start (String)

    The inclusive start key of the query range.

  • :after (String)

    The exclusive start key of the query range.

  • :before (String)

    The exclusive end key of the query range.

  • :end (String)

    The inclusive end key of the query range.

Returns:

  • Orchestrate::API::CollectionResponse

Raises:

  • Orchestrate::API::InvalidSearchParam The :limit value is not valid.



249
250
251
252
# File 'lib/orchestrate/client.rb', line 249

def list(collection, options={})
  API::Helpers.range_keys!('key', options)
  send_request :get, [collection], { query: options, response: API::CollectionResponse }
end

#list_events(collection, key, event_type, options = {}) ⇒ Object

Lists events associated with a key. Results are time-ordered in reverse-chronological order, and paginated.

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • event_type (#to_s)

    The category for the event.

  • options (Hash) (defaults to: {})

    The parameters for the query.

Options Hash (options):

  • :limit (Integer) — default: 10

    The number of results to return. Default 100.

  • :start (Date, Time, String, Integer)

    The inclusive start of the range.

  • :after (Date, Time, String, Integer)

    The exclusive start of the range.

  • :before (Date, Time, String, Integer)

    The exclusive end of the range.

  • :end (Date, Time, String, Integer)

    The inclusive end of the range.

Returns:

  • Orchestrate::API::CollectionResponse

Raises:

  • Orchestrate::API::NotFound If the provided collection/key doesn't exist.



349
350
351
352
353
354
355
356
# File 'lib/orchestrate/client.rb', line 349

def list_events(collection, key, event_type, options={})
  (options.keys & [:start, :after, :before, :end]).each do |param|
    options[param] = API::Helpers.timestamp(options[param])
  end
  API::Helpers.range_keys!('event', options)
  path = [collection, key, 'events', event_type]
  send_request :get, path, { query: options, response: API::CollectionResponse }
end

#list_refs(collection, key, options = {}) ⇒ Object

List historical refs for values of a key. Results are time-ordered newest-to-oldest and paginated.

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • options (Hash) (defaults to: {})

    Parameters for the query.

Options Hash (options):

  • :limit (Integer) — default: 10

    The number of results to return. Maximum 100.

  • :offset (Integer) — default: 0

    The starting position of the results.

  • :values (true, false) — default: false

    Whether to return the value for each ref. Refs with no content (for example, deleted with #delete) will not have a value, but marked with a 'tombstone' => true key.

Returns:

  • Orchestrate::API::CollectionResponse

Raises:

  • Orchestrate::API::NotFound If there are no values for the provided key/collection.

  • Orchestrate::API::InvalidSearchParam The :limit/:offset values are not valid.

  • Orchestrate::API::MalformedRef If the ref provided is not a valid ref.



120
121
122
# File 'lib/orchestrate/client.rb', line 120

def list_refs(collection, key, options={})
  send_request :get, [collection, key, :refs], { query: options, response: API::CollectionResponse }
end

#patch(collection, key, body, condition = nil) ⇒ Object

Manipulate values associated with a key, without retrieving the key object. Array of operations passed as body, will execute operations on the key sequentially. Patch.

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • body (#to_json)

    The value for the key.

  • condition (String, false, nil) (defaults to: nil)

    Conditions for setting the value. If String, value used as If-Match, value will only be updated if key's current value's ref matches. If nil (default), value is set regardless.

Returns:

  • Orchestrate::API::ItemResponse

Raises:

  • Orchestrate::API::BadRequest the body is not valid JSON.

  • Orchestrate::API::AlreadyPresent the false condition was given, but a value already exists for this collection/key combo.



181
182
183
184
185
186
187
# File 'lib/orchestrate/client.rb', line 181

def patch(collection, key, body, condition=nil)
  headers = {'Content-Type' => 'application/json-patch+json'}
  if condition.is_a?(String)
    headers['If-Match'] = API::Helpers.format_ref(condition)
  end
  send_request :patch, [collection, key], { body: body, headers: headers, response: API::ItemResponse }
end

#patch_merge(collection, key, body, condition = nil) ⇒ Object

Merge field/value pairs into existing key, without retrieving the key object. Patch. If a given field's value is nil, will remove field from existing key on merge.

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • body (#to_json)

    The value for the key.

  • condition (String, false, nil) (defaults to: nil)

    Conditions for setting the value. If String, value used as If-Match, value will only be updated if key's current value's ref matches. If nil (default), value is set regardless.

Returns:

  • Orchestrate::API::ItemResponse

Raises:

  • Orchestrate::API::BadRequest the body is not valid JSON.

  • Orchestrate::API::AlreadyPresent the false condition was given, but a value already exists for this collection/key combo.



201
202
203
204
205
206
207
# File 'lib/orchestrate/client.rb', line 201

def patch_merge(collection, key, body, condition=nil)
  headers = {'Content-Type' => 'application/merge-patch+json'}
  if condition.is_a?(String)
    headers['If-Match'] = API::Helpers.format_ref(condition)
  end
  send_request :patch, [collection, key], { body: body, headers: headers, response: API::ItemResponse }
end

#pingObject

Tests authentication with Orchestrate.

Returns:

  • Orchestrate::API::Response

Raises:

  • Orchestrate::API::Unauthorized if the client could not authenticate.



56
57
58
# File 'lib/orchestrate/client.rb', line 56

def ping
  send_request :head, []
end

#post(collection, body) ⇒ Object

Parameters:

  • collection (#to_s)

    The name of the collection.

  • body (#to_json)

    The value to store.

Returns:

  • Orchestrate::API::ItemResponse



129
130
131
# File 'lib/orchestrate/client.rb', line 129

def post(collection, body)
  send_request :post, [collection], { body: body, response: API::ItemResponse }
end

#post_event(collection, key, event_type, body, timestamp = nil) ⇒ Object

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • event_type (#to_s)

    The category for the event.

  • body (#to_json)

    The value for the event.

  • timestamp (Time, Date, Integer, String, nil) (defaults to: nil)

    The timestamp for the event. If omitted, the timestamp is created by Orchestrate. If a String, must match the Timestamp specification.

Returns:

  • Orchestrate::API::ItemResponse

Raises:

  • Orchestrate::API::BadRequest If the body is not valid JSON.



283
284
285
286
287
# File 'lib/orchestrate/client.rb', line 283

def post_event(collection, key, event_type, body, timestamp=nil)
  timestamp = API::Helpers.timestamp(timestamp)
  path = [collection, key, 'events', event_type, timestamp].compact
  send_request :post, path, { body: body, response: API::ItemResponse }
end

#purge(collection, key, ref = nil) ⇒ Object

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • ref (#to_s) (defaults to: nil)

    If specified, purges the ref only if the current value's ref matches.

Returns:

  • Orchestrate::API::Response



229
230
231
232
233
# File 'lib/orchestrate/client.rb', line 229

def purge(collection, key, ref=nil)
  headers = {}
  headers['If-Match'] = API::Helpers.format_ref(ref) if ref
  send_request :delete, [collection, key], { query: { purge: true }, headers: headers }
end

#purge_event(collection, key, event_type, timestamp, ordinal, ref = nil) ⇒ Object

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • event_type (#to_s)

    The category for the event.

  • timestamp (Time, Date, Integer, String, nil)

    The timestamp for the event. If a String, must match the Timestamp specification and include the milliseconds portion.

  • ordinal (Integer, #to_s)

    The ordinal for the event in the given timestamp.

  • ref (nil, #to_s) (defaults to: nil)

    If provided, used as If-Match, and event will only update if its current value has the provided ref. If omitted, updates the event regardless.

Returns:

  • Orchestrate::API::Response

Raises:

  • Orchestrate::API::VersionMismatch The event's current value has a ref that does not match the provided ref.

  • Orchestrate::API::MalformedRef If the ref provided is not a valid ref.



328
329
330
331
332
333
334
# File 'lib/orchestrate/client.rb', line 328

def purge_event(collection, key, event_type, timestamp, ordinal, ref=nil)
  timestamp = API::Helpers.timestamp(timestamp)
  path = [collection, key, 'events', event_type, timestamp, ordinal]
  headers = {}
  headers['If-Match'] = API::Helpers.format_ref(ref) if ref
  send_request :delete, path, { query: { purge: true }, headers: headers }
end

#put(collection, key, body, condition = nil) ⇒ Object Also known as: put_if_unmodified

Updates the value associated with a key. If the key does not currently have a value, will create the value.

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • body (#to_json)

    The value for the key.

  • condition (String, false, nil) (defaults to: nil)

    Conditions for setting the value. If String, value used as If-Match, value will only be updated if key's current value's ref matches. If false, uses If-None-Match the value will only be set if there is no existent value for the key. If nil (default), value is set regardless.

Returns:

  • Orchestrate::API::ItemResponse

Raises:

  • Orchestrate::API::BadRequest the body is not valid JSON.

  • Orchestrate::API::IndexingConflict One of the value's keys contains a value of a different type than the schema that exists for the collection.

  • Orchestrate::API::VersionMismatch A ref was provided, but it does not match the ref for the current value.

  • Orchestrate::API::AlreadyPresent the false condition was given, but a value already exists for this collection/key combo.

See Also:



151
152
153
154
155
156
157
158
159
# File 'lib/orchestrate/client.rb', line 151

def put(collection, key, body, condition=nil)
  headers={}
  if condition.is_a?(String)
    headers['If-Match'] = API::Helpers.format_ref(condition)
  elsif condition == false
    headers['If-None-Match'] = '"*"'
  end
  send_request :put, [collection, key], { body: body, headers: headers, response: API::ItemResponse }
end

#put_event(collection, key, event_type, timestamp, ordinal, body, ref = nil) ⇒ Object

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • event_type (#to_s)

    The category for the event.

  • timestamp (Time, Date, Integer, String, nil)

    The timestamp for the event. If a String, must match the Timestamp specification and include the milliseconds portion.

  • ordinal (Integer, #to_s)

    The ordinal for the event in the given timestamp.

  • body (#to_json)

    The value for the event.

  • ref (nil, #to_s) (defaults to: nil)

    If provided, used as If-Match, and event will only update if its current value has the provided ref. If omitted, updates the event regardless.

Returns:

  • Orchestrate::API::ItemResponse

Raises:

  • Orchestrate::API::NotFound The specified event doesn't exist.

  • Orchestrate::API::BadRequest If the body is not valid JSON.

  • Orchestrate::API::VersionMismatch The event's current value has a ref that does not match the provided ref.

  • Orchestrate::API::MalformedRef If the ref provided is not a valid ref.



306
307
308
309
310
311
312
# File 'lib/orchestrate/client.rb', line 306

def put_event(collection, key, event_type, timestamp, ordinal, body, ref=nil)
  timestamp = API::Helpers.timestamp(timestamp)
  path = [collection, key, 'events', event_type, timestamp, ordinal]
  headers = {}
  headers['If-Match'] = API::Helpers.format_ref(ref) if ref
  send_request :put, path, { body: body, headers: headers, response: API::ItemResponse }
end

#put_if_absent(collection, key, body) ⇒ Object

See Also:



165
166
167
# File 'lib/orchestrate/client.rb', line 165

def put_if_absent(collection, key, body)
  put collection, key, body, false
end

#put_relation(collection, key, kind, to_collection, to_key) ⇒ Object

Creates a relationship between two Key/Value objects. Relations can span collections.

Parameters:

  • collection (#to_s)

    The name of the collection.

  • key (#to_s)

    The name of the key.

  • kind (#to_s)

    The kind of relationship to create.

  • to_collection (#to_s)

    The name of the collection to relate to.

  • to_key (#to_s)

    The name of the key to relate to.

Returns:

  • Orchestrate::API::Response

Raises:

  • Orchestrate::API::NotFound If either end of the relation doesn't exist.



386
387
388
# File 'lib/orchestrate/client.rb', line 386

def put_relation(collection, key, kind, to_collection, to_key)
  send_request :put, [collection, key, 'relation', kind, to_collection, to_key], { body: {} }
end

#search(collection, query, options = {}) ⇒ Object

Search the items in a collection using a Lucene Query Syntax.

Parameters:

  • collection (#to_s)

    The name of the collection

  • query (String)

    The Lucene Query String to query the collection with.

  • options (Hash) (defaults to: {})

    Parameters for the query

Options Hash (options):

  • :limit (Integer) — default: 10

    The number of results to return. Maximum 100.

  • :offset (Integer) — default: 0

    The starting position of the results.

  • :sort (String)

    The field and direction to sort by. Ex: value.name:asc

Returns:

  • Orchestrate::API::CollectionResponse

Raises:

  • Orchestrate::API::InvalidSearchParam The :limit/:offset values are not valid.

  • Orchestrate::API::SearchQueryMalformed if query isn't a valid Lucene query.



75
76
77
78
# File 'lib/orchestrate/client.rb', line 75

def search(collection, query, options={})
  send_request :get, [collection], { query: options.merge({query: query}),
                                     response: API::CollectionResponse }
end

#send_request(method, path, options = {}) ⇒ Object

Performs an HTTP request against the Orchestrate API

Parameters:

  • method (Symbol)

    The HTTP method - :get, :post, :put, :delete

  • path (Array<#to_s>)

    Path segments for the request's URI. Prepended by 'v0'.

  • options (Hash) (defaults to: {})

    extra parameters

Options Hash (options):

  • :query (Hash) — default: {}

    Query String parameters.

  • :body (#to_json) — default: ''

    The request body.

  • :headers (Hash) — default: {}

    Extra request headers.

  • :response (Class) — default: Orchestrate::API::Response

    A subclass of Orchestrate::API::Response to instantiate and return

Returns:

  • API::Response

Raises:



434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/orchestrate/client.rb', line 434

def send_request(method, path, options={})
  path = ['/v0'].concat(path.map{|s| URI.escape(s.to_s).gsub('/','%2F') }).join('/')
  query_string = options.fetch(:query, {})
  body = options.fetch(:body, '')
  headers = options.fetch(:headers, {})
  headers['User-Agent'] = "ruby/orchestrate/#{Orchestrate::VERSION}"
  headers['Accept'] = 'application/json' if method == :get
  headers['Connection'] = 'close' if method == :head

  http_response = http.send(method) do |request|
    request.url path, query_string
    if [:put, :post].include?(method)
      headers['Content-Type'] = 'application/json'
      request.body = body.to_json
    elsif [:patch].include?(method)
      request.body = body.to_json
    end
    headers.each {|header, value| request[header] = value }
  end

  response_class = options.fetch(:response, API::Response)
  response_class.new(http_response, self)
end