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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: de6f6df430a397bac87d65a6c13679705f5de9ab
4
- data.tar.gz: 7bc42ee2220a8dec27c24689922a3989a9e03f25
3
+ metadata.gz: 896e56c8c6989701ac79594cb43f20cc16e64947
4
+ data.tar.gz: a508bdaa4ee26fb3effc1aa70718439f40f026c2
5
5
  SHA512:
6
- metadata.gz: bbb8f4f4a9f18f5f1d245eaef876e7d726ca1009a23fab327de7d860742dc8fbb3174afb103a66c6fe4909fbfdc6273b8e2f205de9ab532e97d09b25f1a80470
7
- data.tar.gz: 8d2a0dd9bc39b57c51f0b4058ed0980062d76bf1cae40c94a51ed2eb479f696cb52825a2a3cd139cdf0629c902701259e94119359de3436ad928a5e66f12b687
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::Configuration.api_key = '8ce391a...'
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] = #<Faraday::Response:0x00...>
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.configure do |config|
83
- config.faraday = {|f| f.adapter :typhoeus }
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.configure do |config|
97
- config.faraday = {|f| f.adapter :em_http }
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-Syncrony
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.configure do |config|
110
- config.faraday = {|f| f.adapter :em_synchrony }
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
- # Not used. But it does contain nice summary of orchestrate.io api errors.
4
- #
5
- module Error
6
- def errors
7
- @@errors ||= [
8
- { :status => 400,
9
- :code => :api_bad_request,
10
- :desc => 'The API request is malformed.'
11
- },
12
- { :status => 500,
13
- :code => :security_authentication,
14
- :desc => 'An error occurred while trying to authenticate.'
15
- },
16
- { :status => 401,
17
- :code => :security_unauthorized,
18
- :desc => 'Valid credentials are required.'
19
- },
20
- { :status => 400,
21
- :code => :search_param_invalid,
22
- :desc => 'A provided search query param is invalid.'
23
- },
24
- { :status => 500,
25
- :code => :search_index_not_found,
26
- :desc => 'Index could not be queried for this application.'
27
- },
28
- { :status => 500,
29
- :code => :internal_error,
30
- :desc => 'Internal Error.'
31
- },
32
- { :status => 404,
33
- :code => :items_not_found,
34
- :desc => 'The requested items could not be found.'
35
- },
36
- { :status => 412,
37
- :code => :item_version_mismatch,
38
- :desc => 'The version of the item does not match.'
39
- },
40
- { :status => 412,
41
- :code => :item_already_present,
42
- :desc => 'The item is already present.'
43
- },
44
- { :status => 400,
45
- :code => :item_ref_malformed,
46
- :desc => 'The provided Item Ref is malformed.'
47
- },
48
- { :status => 409,
49
- :code => :indexing_conflict,
50
- :desc => 'The item has been stored but conflicts were detected ' +
51
- 'when indexing. Conflicting fields have not been indexed.'
52
- },
53
- ]
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
- end
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
@@ -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'