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.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/README.md +28 -20
- data/lib/orchestrate/api/errors.rb +114 -52
- data/lib/orchestrate/api/helpers.rb +48 -0
- data/lib/orchestrate/api/response.rb +111 -0
- data/lib/orchestrate/api.rb +4 -6
- data/lib/orchestrate/client.rb +290 -302
- data/lib/orchestrate/version.rb +1 -1
- data/lib/orchestrate.rb +0 -37
- data/test/orchestrate/api/collections_test.rb +28 -6
- data/test/orchestrate/api/event_test.rb +85 -20
- data/test/orchestrate/api/exceptions_test.rb +172 -0
- data/test/orchestrate/api/graph_test.rb +4 -0
- data/test/orchestrate/api/key_value_test.rb +52 -7
- data/test/orchestrate/api/response_test.rb +36 -0
- data/test/orchestrate/client_test.rb +15 -16
- data/test/test_helper.rb +4 -9
- metadata +10 -6
- data/lib/orchestrate/configuration.rb +0 -53
- data/lib/orchestrate/helpers.rb +0 -22
- data/test/orchestrate/configuration_test.rb +0 -67
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 896e56c8c6989701ac79594cb43f20cc16e64947
|
4
|
+
data.tar.gz: a508bdaa4ee26fb3effc1aa70718439f40f026c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b5eb9d59dabc325fa6c694bf5770b32bf1b002e03395efa4429737aa115efe9219df7e8d276a7c2522d2bdaad4dd7d04d3a14fe1507216211e3857ccf5343c1
|
7
|
+
data.tar.gz: 37e4a6d198dc537124d01efd46be0bf90e78a140e6c837f61039c94ac5403f9272ff50a91eb0ae8ba22a8b77ad90ae9ca089976cff3fdf8c0351c1edc52ddc51
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-m markdown - LICENSE
|
data/README.md
CHANGED
@@ -11,13 +11,7 @@ Ruby client interface for the [Orchestrate.io](http://orchestrate.io) REST API.
|
|
11
11
|
Provide your API key:
|
12
12
|
|
13
13
|
``` ruby
|
14
|
-
Orchestrate::
|
15
|
-
```
|
16
|
-
|
17
|
-
Create a client:
|
18
|
-
|
19
|
-
``` ruby
|
20
|
-
client = Orchestrate::Client.new
|
14
|
+
client = Orchestrate::Client.new('8ce391a...')
|
21
15
|
```
|
22
16
|
|
23
17
|
and start making requests:
|
@@ -43,7 +37,7 @@ You may use Faraday's `test` adapter to stub out calls to the Orchestrate API in
|
|
43
37
|
If you're using a Faraday backend that enables parallelization, such as Typhoeus, EM-HTTP-Request, or EM-Synchrony you can use `Orchestrate::Client#in_parallel` to fire off multiple requests at once. If your Faraday backend does not support this, the method will still work as expected, but Faraday will output a warning to STDERR and the requests will be performed in series.
|
44
38
|
|
45
39
|
``` ruby
|
46
|
-
client = Orchestrate::Client.new
|
40
|
+
client = Orchestrate::Client.new(api_key)
|
47
41
|
|
48
42
|
responses = client.in_parallel do |r|
|
49
43
|
r[:list] = client.list(:my_collection)
|
@@ -52,7 +46,7 @@ responses = client.in_parallel do |r|
|
|
52
46
|
end
|
53
47
|
# will return when all requests have completed
|
54
48
|
|
55
|
-
responses[:user] = #<
|
49
|
+
responses[:user] = #<Orchestrate::API::ItemResponse:0x00...>
|
56
50
|
```
|
57
51
|
|
58
52
|
### Callback Support
|
@@ -60,7 +54,6 @@ responses[:user] = #<Faraday::Response:0x00...>
|
|
60
54
|
If you're using a Faraday backend that enables callbacks, such as EM-HTTP-Request or EM-Synchrony, you may use the callback interface to designate actions to perform when the request completes.
|
61
55
|
|
62
56
|
``` ruby
|
63
|
-
client = Orchestrate::Client.new
|
64
57
|
response = client.list(:my_collection)
|
65
58
|
response.finished? # false
|
66
59
|
response.on_complete do
|
@@ -79,9 +72,8 @@ Typhoeus is backed by libcurl and enables parallelization.
|
|
79
72
|
require 'typhoeus'
|
80
73
|
require 'typhoeus/adapters/faraday'
|
81
74
|
|
82
|
-
Orchestrate.
|
83
|
-
|
84
|
-
config.api_key = "my_api_key"
|
75
|
+
client = Orchestrate::Client.new(api_key) do |conn|
|
76
|
+
conn.adapter :typhoeus
|
85
77
|
end
|
86
78
|
```
|
87
79
|
|
@@ -93,23 +85,39 @@ EM-HTTP-Request is an HTTP client for Event Machine. It enables callback suppor
|
|
93
85
|
``` ruby
|
94
86
|
require 'em-http-request'
|
95
87
|
|
96
|
-
Orchestrate.
|
97
|
-
|
98
|
-
config.api_key = "my_api_key"
|
88
|
+
client = Orchestrate::Client.new(api_key) do |conn|
|
89
|
+
conn.adapter :em_http
|
99
90
|
end
|
100
91
|
```
|
101
92
|
|
102
|
-
### Using with EM-
|
93
|
+
### Using with EM-Synchrony
|
103
94
|
|
104
95
|
EM-Synchrony is a collection of utility classes for EventMachine to help untangle evented code. It enables parallelization.
|
105
96
|
|
106
97
|
``` ruby
|
107
98
|
require 'em-synchrony'
|
108
99
|
|
109
|
-
Orchestrate.
|
110
|
-
|
111
|
-
config.api_key = "my_api_key"
|
100
|
+
client = Orchestrate::Client.new(api_key) do |conn|
|
101
|
+
conn.adapter = f.adapter :em_synchrony
|
112
102
|
end
|
113
103
|
```
|
114
104
|
|
105
|
+
## Release Notes
|
106
|
+
- June 12, 2014: release 0.6.0
|
107
|
+
- **BACKWARDS-INCOMPATIBLE** Reworked Client constructor to take API key and
|
108
|
+
optional Faraday configuration block. See 9045ffc for details.
|
109
|
+
- Migrated documentation to YARD
|
110
|
+
- Provide basic response wrappers specific to generic request types.
|
111
|
+
- Raise Exceptions on error response from Orchestrate API.
|
112
|
+
- Remove custom logger in favor of the option to use Faraday middleware.
|
113
|
+
- Accept Time/Date objects for Timestamp arguments to Event-related methods.
|
114
|
+
|
115
|
+
- May 29, 2014: release 0.5.1
|
116
|
+
- Fix problem with legacy code preventing gem from loading in some environments
|
117
|
+
|
118
|
+
- May 21, 2014: release 0.5.0
|
119
|
+
Initial Port from @jimcar
|
120
|
+
- Uses Faraday HTTP Library as backend, with examples of alternate adapters
|
121
|
+
- Cleanup client method signatures
|
122
|
+
|
115
123
|
|
@@ -1,57 +1,119 @@
|
|
1
1
|
module Orchestrate::API
|
2
2
|
|
3
|
-
#
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
{ :status => 500,
|
25
|
-
:code => :search_index_not_found,
|
26
|
-
:desc => 'Index could not be queried for this application.'
|
27
|
-
},
|
28
|
-
{ :status => 500,
|
29
|
-
:code => :internal_error,
|
30
|
-
:desc => 'Internal Error.'
|
31
|
-
},
|
32
|
-
{ :status => 404,
|
33
|
-
:code => :items_not_found,
|
34
|
-
:desc => 'The requested items could not be found.'
|
35
|
-
},
|
36
|
-
{ :status => 412,
|
37
|
-
:code => :item_version_mismatch,
|
38
|
-
:desc => 'The version of the item does not match.'
|
39
|
-
},
|
40
|
-
{ :status => 412,
|
41
|
-
:code => :item_already_present,
|
42
|
-
:desc => 'The item is already present.'
|
43
|
-
},
|
44
|
-
{ :status => 400,
|
45
|
-
:code => :item_ref_malformed,
|
46
|
-
:desc => 'The provided Item Ref is malformed.'
|
47
|
-
},
|
48
|
-
{ :status => 409,
|
49
|
-
:code => :indexing_conflict,
|
50
|
-
:desc => 'The item has been stored but conflicts were detected ' +
|
51
|
-
'when indexing. Conflicting fields have not been indexed.'
|
52
|
-
},
|
53
|
-
]
|
3
|
+
# Base class for Errors from Orchestrate.
|
4
|
+
class BaseError < StandardError
|
5
|
+
# class-level attr-reader for the error's response code.
|
6
|
+
# @return [Integer] Status code for error.
|
7
|
+
def self.status; @status; end
|
8
|
+
|
9
|
+
# class-level attr-reader for the error's code.
|
10
|
+
# @return [String] API's error code.
|
11
|
+
def self.code; @code; end
|
12
|
+
|
13
|
+
# @return [Faraday::Response] The response that caused the error.
|
14
|
+
attr_reader :response
|
15
|
+
|
16
|
+
# @param response [Faraday::Response] The response that caused the error.
|
17
|
+
def initialize(response)
|
18
|
+
@response = response
|
19
|
+
if response.headers['Content-Type'] == 'application/json'
|
20
|
+
super(response.body['message'])
|
21
|
+
else
|
22
|
+
super(response.body)
|
23
|
+
end
|
54
24
|
end
|
55
25
|
end
|
56
26
|
|
57
|
-
|
27
|
+
# indicates a 4xx-class response, and the problem was with the request.
|
28
|
+
class RequestError < BaseError; end
|
29
|
+
|
30
|
+
# The client provided a malformed request.
|
31
|
+
class BadRequest < RequestError
|
32
|
+
@status = 400
|
33
|
+
@code = 'api_bad_request'
|
34
|
+
end
|
35
|
+
|
36
|
+
# The client provided a malformed search query.
|
37
|
+
class MalformedSearch < RequestError
|
38
|
+
@status = 400
|
39
|
+
@code = 'search_query_malformed'
|
40
|
+
end
|
41
|
+
|
42
|
+
# The client provided a ref value that is not valid.
|
43
|
+
class MalformedRef < RequestError
|
44
|
+
@status = 400
|
45
|
+
@code = 'item_ref_malformed'
|
46
|
+
end
|
47
|
+
|
48
|
+
# When a Search Parameter is recognized, but not valid. For example, a limit
|
49
|
+
# exceeding 100.
|
50
|
+
class InvalidSearchParam < RequestError
|
51
|
+
@status = 400
|
52
|
+
@code = 'search_param_invalid'
|
53
|
+
end
|
54
|
+
|
55
|
+
# A Valid API key was not provided.
|
56
|
+
class Unauthorized < RequestError
|
57
|
+
@status = 401
|
58
|
+
@code = 'security_unauthorized'
|
59
|
+
end
|
60
|
+
|
61
|
+
# Something the user expected to be there was not.
|
62
|
+
class NotFound < RequestError
|
63
|
+
@status = 404
|
64
|
+
@code = 'items_not_found'
|
65
|
+
end
|
66
|
+
|
67
|
+
# When a new collection is created by its first KV document, a schema is created
|
68
|
+
# for indexing, including the types of the values for the KV document. This error
|
69
|
+
# will occur when a new document provides values with different types. Values with
|
70
|
+
# unexpected types will not be indexed. Since search is an implicit feature
|
71
|
+
# of the service, this is an error and worth raising an exception over.
|
72
|
+
#
|
73
|
+
# @example This is the example provided to me by the Orchestrate team:
|
74
|
+
# client.put(:test, :first, { "count" => 0 }) # establishes 'count' as a Long
|
75
|
+
# client.put(:test, :second, { "count" => "none" }) # 'count' is not a Long
|
76
|
+
#
|
77
|
+
class IndexingConflict < RequestError
|
78
|
+
@status = 409
|
79
|
+
@code = 'indexing_conflict'
|
80
|
+
end
|
81
|
+
|
82
|
+
# Client provided a ref that is not the current ref.
|
83
|
+
class VersionMismatch < RequestError
|
84
|
+
@status = 412
|
85
|
+
@code = 'item_version_mismatch'
|
86
|
+
end
|
87
|
+
|
88
|
+
# Client provided "If-None-Match", but something matched.
|
89
|
+
class AlreadyPresent < RequestError
|
90
|
+
@status = 412
|
91
|
+
@code = 'item_already_present'
|
92
|
+
end
|
93
|
+
|
94
|
+
# indicates a 500-class response, the problem is on Orchestrate's end
|
95
|
+
class ServiceError < BaseError; end
|
96
|
+
|
97
|
+
class SecurityAuthentication < ServiceError
|
98
|
+
@status = 500
|
99
|
+
@code = 'security_authentication'
|
100
|
+
end
|
101
|
+
|
102
|
+
class SearchIndexNotFound < ServiceError
|
103
|
+
@status = 500
|
104
|
+
@code = 'search_index_not_found'
|
105
|
+
end
|
106
|
+
|
107
|
+
class InternalError < ServiceError
|
108
|
+
@status = 500
|
109
|
+
@code = 'internal_error'
|
110
|
+
end
|
111
|
+
|
112
|
+
# The full complement of Error classes.
|
113
|
+
ERRORS=[ BadRequest, MalformedSearch, MalformedRef, InvalidSearchParam,
|
114
|
+
Unauthorized, NotFound, IndexingConflict,
|
115
|
+
VersionMismatch, AlreadyPresent,
|
116
|
+
SecurityAuthentication, SearchIndexNotFound, InternalError
|
117
|
+
]
|
118
|
+
|
119
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Orchestrate::API
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# Suffixes a params hash for range bounds with the given type. Modifies the hash in-place.
|
7
|
+
# @param suffix [String] the suffix for the range keys
|
8
|
+
# @param params [Hash{Symbol=>Value}]
|
9
|
+
# @option params :start The inclusive start of the range
|
10
|
+
# @option params :after The exclusive start of the range
|
11
|
+
# @option params :before The exclusive end of the range
|
12
|
+
# @option params :end The inclusive end of the range
|
13
|
+
# @example Transforming an Event Range
|
14
|
+
# keys = { start: Value }
|
15
|
+
# Orchestrate::Helpers.range_keys!('event', keys)
|
16
|
+
# keys #=> { startEvent: Value }
|
17
|
+
def range_keys!(suffix, params)
|
18
|
+
suffix = suffix.capitalize
|
19
|
+
[:start, :end, :before, :after].each do |key|
|
20
|
+
if params[key]
|
21
|
+
params["#{key}#{suffix}"] = params[key]
|
22
|
+
params.delete(key)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Coerces a Date or Time object to Integer Milliseconds, per the Timestamps documentation:
|
28
|
+
# http://orchestrate.io/docs/api/#events/timestamps
|
29
|
+
# If provided a value other than Date or Time, will return it untouched.
|
30
|
+
# @overload timestamp(time)
|
31
|
+
# @param time [Date, Time] The timestamp in Date or Time form.
|
32
|
+
# @return [Integer] The timestamp in integer milliseconds since epoch.
|
33
|
+
def timestamp(time)
|
34
|
+
time = time.to_time if time.kind_of?(Date)
|
35
|
+
time = (time.getutc.to_f * 1000).to_i if time.kind_of?(Time)
|
36
|
+
time
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
# Formats the provided 'ref' to be quoted per API specification.
|
41
|
+
# @param ref [String] The ref desired.
|
42
|
+
# @return [String] The ref, quoted. If already quoted, does not double-quote.
|
43
|
+
def format_ref(ref)
|
44
|
+
"\"#{ref.gsub(/"/,'')}\""
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'webrick'
|
3
|
+
|
4
|
+
module Orchestrate::API
|
5
|
+
|
6
|
+
# A generic response from the API.
|
7
|
+
class Response
|
8
|
+
|
9
|
+
extend Forwardable
|
10
|
+
def_delegators :@response, :status, :body, :headers, :success?, :finished?, :on_complete
|
11
|
+
# @!attribute [r] status
|
12
|
+
# @return [Integer] The HTTP Status code of the response.
|
13
|
+
# @!attribute [r] body
|
14
|
+
# @return [Hash, String] The response body, de-serialized from JSON.
|
15
|
+
# @!attribute [r] headers
|
16
|
+
# @return [Hash{String => String}] The response headers
|
17
|
+
# @!method success?()
|
18
|
+
# @return [true, false] If the response is 1xx-3xx class response or a 4xx-5xx class response.
|
19
|
+
# @!method finished?()
|
20
|
+
# @return [true, false] If the response is finished or not.
|
21
|
+
# @!method on_complete()
|
22
|
+
# @yield [block] A block to be called when the response has completed.
|
23
|
+
|
24
|
+
# @return [String] The Orchestrate API Request ID. For use when troubleshooting.
|
25
|
+
attr_reader :request_id
|
26
|
+
|
27
|
+
# @return [Time] The time at which the response was made.
|
28
|
+
attr_reader :request_time
|
29
|
+
|
30
|
+
# @return [Orchestrate::Client] The client used to generate the response.
|
31
|
+
attr_reader :client
|
32
|
+
|
33
|
+
# Instantiate a new Respose
|
34
|
+
# @param faraday_response [Faraday::Response] The Faraday response object.
|
35
|
+
# @param client [Orchestrate::Client] The client used to generate the response.
|
36
|
+
def initialize(faraday_response, client)
|
37
|
+
@client = client
|
38
|
+
@response = faraday_response
|
39
|
+
@request_id = headers['X-Orchestrate-Req-Id']
|
40
|
+
@request_time = Time.parse(headers['Date'])
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
# A generic response for a single entity (K/V, Ref, Event)
|
46
|
+
class ItemResponse < Response
|
47
|
+
|
48
|
+
# @return [String] The 'Location' of the item.
|
49
|
+
attr_reader :location
|
50
|
+
|
51
|
+
# @return [String] The canonical 'ref' of the item.
|
52
|
+
attr_reader :ref
|
53
|
+
|
54
|
+
# (see Orchestrate::API::Response#initialize)
|
55
|
+
def initialize(faraday_response, client)
|
56
|
+
super(faraday_response, client)
|
57
|
+
@location = headers['Content-Location'] || headers['Location']
|
58
|
+
@ref = headers.fetch('Etag','').gsub('"','')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# A generic response for a collection of entities (K/V, Refs, Events, Search)
|
63
|
+
class CollectionResponse < Response
|
64
|
+
|
65
|
+
# @return [Integer] The number of items in this response
|
66
|
+
attr_reader :count
|
67
|
+
|
68
|
+
# @return [Integer, nil] If provided, the number of total items for this collection
|
69
|
+
attr_reader :total_count
|
70
|
+
|
71
|
+
# @return [Array] The items in the response.
|
72
|
+
attr_reader :results
|
73
|
+
|
74
|
+
# @return [String] The location for the next page of results
|
75
|
+
attr_reader :next_link
|
76
|
+
|
77
|
+
# @return [String] The location for the previous page of results
|
78
|
+
attr_reader :prev_link
|
79
|
+
|
80
|
+
# (see Orchestrate::API::Response#initialize)
|
81
|
+
def initialize(faraday_response, client)
|
82
|
+
super(faraday_response, client)
|
83
|
+
@count = body['count']
|
84
|
+
@total_count = body['total_count']
|
85
|
+
@results = body['results']
|
86
|
+
@next_link = body['next']
|
87
|
+
@prev_link = body['prev']
|
88
|
+
end
|
89
|
+
|
90
|
+
# Retrieves the next page of results, if available
|
91
|
+
# @return [nil, Orchestrate::API::CollectionResponse]
|
92
|
+
def next_results
|
93
|
+
fire_request(next_link)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Retrieves the previous page of results, if available
|
97
|
+
# @return [nil, Orchestrate::API::CollectionResponse]
|
98
|
+
def previous_results
|
99
|
+
fire_request(prev_link)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
def fire_request(link)
|
104
|
+
return nil unless link
|
105
|
+
uri = URI(link)
|
106
|
+
params = WEBrick::HTTPUtils.parse_query(uri.query)
|
107
|
+
path = uri.path.split("/")[2..-1]
|
108
|
+
@client.send_request(:get, path, { query: params, response: self.class })
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/orchestrate/api.rb
CHANGED
@@ -2,13 +2,11 @@ require "orchestrate"
|
|
2
2
|
|
3
3
|
module Orchestrate
|
4
4
|
|
5
|
-
#
|
6
|
-
# Ruby gem +orchestrate-api+ provides an interface to the
|
7
|
-
# [Orchestrate](http://orchestrate.io) API.
|
8
|
-
#
|
9
|
-
# The Client class is used to setup the client and make HTTP requests.
|
10
|
-
#
|
11
5
|
module API
|
12
6
|
end
|
13
7
|
|
14
8
|
end
|
9
|
+
|
10
|
+
require_relative 'api/response'
|
11
|
+
require_relative 'api/errors'
|
12
|
+
require_relative 'api/helpers'
|