orchestrate 0.5.1 → 0.6.0

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