api_client 0.5.24-java → 0.6.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/lib/api_client/base.rb +77 -0
- data/lib/api_client/connection/abstract.rb +81 -0
- data/lib/api_client/connection/basic.rb +131 -0
- data/lib/api_client/connection/json.rb +14 -0
- data/lib/api_client/connection/middlewares/request/json.rb +34 -0
- data/lib/api_client/connection/middlewares/request/logger.rb +64 -0
- data/lib/api_client/connection/middlewares/request/oauth.rb +22 -0
- data/lib/api_client/connection/oauth.rb +18 -0
- data/lib/api_client/errors.rb +32 -0
- data/lib/api_client/mixins/configuration.rb +24 -0
- data/lib/api_client/mixins/connection_hooks.rb +24 -0
- data/lib/api_client/mixins/delegation.rb +23 -0
- data/lib/api_client/mixins/inheritance.rb +19 -0
- data/lib/api_client/mixins/instantiation.rb +29 -0
- data/lib/api_client/mixins/scoping.rb +49 -0
- data/lib/api_client/resource/base.rb +67 -0
- data/lib/api_client/resource/name_resolver.rb +37 -0
- data/lib/api_client/resource/scope.rb +73 -0
- data/lib/api_client/scope.rb +125 -0
- data/lib/api_client/utils.rb +18 -0
- data/lib/api_client/version.rb +3 -0
- data/spec/api_client/base/connection_hook_spec.rb +18 -0
- data/spec/api_client/base/delegation_spec.rb +15 -0
- data/spec/api_client/base/inheritance_spec.rb +44 -0
- data/spec/api_client/base/instantiation_spec.rb +55 -0
- data/spec/api_client/base/marshalling_spec.rb +33 -0
- data/spec/api_client/base/parsing_spec.rb +38 -0
- data/spec/api_client/base/scoping_spec.rb +60 -0
- data/spec/api_client/base_spec.rb +107 -0
- data/spec/api_client/connection/abstract_spec.rb +21 -0
- data/spec/api_client/connection/basic_spec.rb +198 -0
- data/spec/api_client/connection/oauth_spec.rb +23 -0
- data/spec/api_client/connection/request/json_spec.rb +30 -0
- data/spec/api_client/connection/request/logger_spec.rb +18 -0
- data/spec/api_client/connection/request/oauth_spec.rb +26 -0
- data/spec/api_client/resource/base_spec.rb +97 -0
- data/spec/api_client/resource/name_spec.rb +19 -0
- data/spec/api_client/resource/scope_spec.rb +122 -0
- data/spec/api_client/scope_spec.rb +204 -0
- data/spec/api_client/utils_spec.rb +32 -0
- data/spec/support/matchers.rb +5 -0
- metadata +72 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 861da114f0da9d319e5e08c020e4c07f29299fb111975feeec2b926142f7d1f5
|
4
|
+
data.tar.gz: f4faf72c964e354f05ff9bafe9da81385f749453130c21877081cf3b73dae220
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c928fcc0591ec30b435c8e135b583228e302282031dcc5c8122f7edcd3987010ddb43b8954b3c169a89e77a43d50ce4e898c0c3ec1ea93b539b85f1bf3c94d39
|
7
|
+
data.tar.gz: 1601de7481ad45bbdce864ec4291197721b222dafbc0b119dd739a26fc38ece6a96a728b40f038809ae886fb89498328a699245b901a798f703a9a2d23a3051c
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# 0.6.0
|
2
|
+
|
3
|
+
* Add support for faraday gem version >= 1.0.0 and < 2.0.0
|
4
|
+
* Drop support for Ruby < 2.3
|
5
|
+
|
6
|
+
# 0.5.26
|
7
|
+
|
8
|
+
* Add support for HTTP status code: 412 Precondition Failed
|
9
|
+
|
10
|
+
# 0.5.25
|
11
|
+
|
12
|
+
* Fix broken gem build (gemspec files)
|
13
|
+
|
1
14
|
# 0.5.23
|
2
15
|
|
3
16
|
* Add support for HTTP status code: 423 Locked
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module ApiClient
|
2
|
+
|
3
|
+
class Base < Hashie::Mash
|
4
|
+
|
5
|
+
extend ApiClient::Mixins::Inheritance
|
6
|
+
extend ApiClient::Mixins::Instantiation
|
7
|
+
extend ApiClient::Mixins::Scoping
|
8
|
+
extend ApiClient::Mixins::ConnectionHooks
|
9
|
+
|
10
|
+
class << self
|
11
|
+
extend ApiClient::Mixins::Delegation
|
12
|
+
extend ApiClient::Mixins::Configuration
|
13
|
+
|
14
|
+
delegate :fetch, :get, :put, :post, :patch, :delete, :headers, :endpoint, :options, :adapter, :params, :raw, :to => :scope
|
15
|
+
|
16
|
+
dsl_accessor :format, :namespace
|
17
|
+
|
18
|
+
def subkey_class
|
19
|
+
Hashie::Mash
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse(response)
|
23
|
+
if response.is_a?(Faraday::Response)
|
24
|
+
return nil if response.status == 204
|
25
|
+
response = response.body
|
26
|
+
end
|
27
|
+
|
28
|
+
if self.format == :json
|
29
|
+
MultiJson.load(response)
|
30
|
+
elsif self.format == :xml
|
31
|
+
MultiXml.parse(response)
|
32
|
+
else
|
33
|
+
response
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# Defaults
|
40
|
+
self.format :json
|
41
|
+
|
42
|
+
def id
|
43
|
+
self['id']
|
44
|
+
end
|
45
|
+
|
46
|
+
def inspect
|
47
|
+
attributes = []
|
48
|
+
attr_keys = self.keys - ['id']
|
49
|
+
attributes.push "id: #{self.id}" if self.id
|
50
|
+
attr_keys.each do |key|
|
51
|
+
attributes.push("#{key}: #{self[key].inspect}")
|
52
|
+
end
|
53
|
+
"#<#{self.class} #{attributes.join(', ')}>"
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def method_missing(method_name, *args, &blk)
|
58
|
+
if respond_to?(method_name) || has_special_ending?(method_name)
|
59
|
+
super
|
60
|
+
elsif use_strict_reader?(method_name)
|
61
|
+
fetch(method_name)
|
62
|
+
else
|
63
|
+
super
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def use_strict_reader?(method_name)
|
68
|
+
respond_to?(:strict_attr_reader?) &&
|
69
|
+
self.strict_attr_reader? &&
|
70
|
+
method_name != :to_ary
|
71
|
+
end
|
72
|
+
|
73
|
+
def has_special_ending?(name)
|
74
|
+
name.to_s =~ /[?=]$/
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ApiClient
|
2
|
+
|
3
|
+
module Connection
|
4
|
+
|
5
|
+
class Abstract
|
6
|
+
|
7
|
+
attr_accessor :endpoint, :handler, :options
|
8
|
+
|
9
|
+
def initialize(endpoint, options = {})
|
10
|
+
raise "Cannot instantiate abstract class" if self.class == ApiClient::Connection::Abstract
|
11
|
+
@endpoint = endpoint
|
12
|
+
@options = options
|
13
|
+
create_handler
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_handler
|
17
|
+
end
|
18
|
+
|
19
|
+
#### ApiClient::Connection::Abstract#get
|
20
|
+
# Performs a GET request
|
21
|
+
# Accepts three parameters:
|
22
|
+
#
|
23
|
+
# * path - the path the request should go to
|
24
|
+
# * data - (optional) the query, passed as a hash and converted into query params
|
25
|
+
# * headers - (optional) headers sent along with the request
|
26
|
+
#
|
27
|
+
def get(path, data = {}, headers = {})
|
28
|
+
end
|
29
|
+
|
30
|
+
#### ApiClient::Connection::Abstract#post
|
31
|
+
# Performs a POST request
|
32
|
+
# Accepts three parameters:
|
33
|
+
#
|
34
|
+
# * path - the path request should go to
|
35
|
+
# * data - (optional) data sent in the request
|
36
|
+
# * headers - (optional) headers sent along in the request
|
37
|
+
#
|
38
|
+
def post(path, data = {}, headers = {})
|
39
|
+
end
|
40
|
+
|
41
|
+
#### ApiClient::Connection::Abstract#patch
|
42
|
+
# Performs a PATCH request
|
43
|
+
# Accepts three parameters:
|
44
|
+
#
|
45
|
+
# * path - the path request should go to
|
46
|
+
# * data - (optional) data sent in the request
|
47
|
+
# * headers - (optional) headers sent along in the request
|
48
|
+
#
|
49
|
+
def patch(path, data = {}, headers = {})
|
50
|
+
end
|
51
|
+
|
52
|
+
#### ApiClient::Connection::Abstract#put
|
53
|
+
# Performs a PUT request
|
54
|
+
# Accepts three parameters:
|
55
|
+
#
|
56
|
+
# * path - the path request should go to
|
57
|
+
# * data - (optional) data sent in the request
|
58
|
+
# * headers - (optional) headers sent along in the request
|
59
|
+
#
|
60
|
+
def put(path, data = {}, headers = {})
|
61
|
+
end
|
62
|
+
|
63
|
+
#### FS::Connection#delete
|
64
|
+
# Performs a DELETE request
|
65
|
+
# Accepts three parameters:
|
66
|
+
#
|
67
|
+
# * path - the path request should go to
|
68
|
+
# * data - (optional) the query, passed as a hash and converted into query params
|
69
|
+
# * headers - (optional) headers sent along in the request
|
70
|
+
#
|
71
|
+
def delete(path, data = {}, headers = {})
|
72
|
+
end
|
73
|
+
|
74
|
+
def inspect
|
75
|
+
"#<#{self.class} endpoint: \"#{endpoint}\">"
|
76
|
+
end
|
77
|
+
alias :to_s :inspect
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module ApiClient
|
2
|
+
|
3
|
+
module Connection
|
4
|
+
|
5
|
+
class Basic < Abstract
|
6
|
+
|
7
|
+
def create_handler
|
8
|
+
# Create and memoize the connection object
|
9
|
+
# The empty block is necessary as we don't want Faraday to
|
10
|
+
# initialize itself, we build our own stack in finalize_handler
|
11
|
+
@handler = Faraday.new(@endpoint, @options[:faraday] || {}) do end
|
12
|
+
finalize_handler
|
13
|
+
end
|
14
|
+
|
15
|
+
def finalize_handler
|
16
|
+
@handler.use Middlewares::Request::Logger, ApiClient.logger if ApiClient.logger
|
17
|
+
@handler.use Faraday::Request::UrlEncoded
|
18
|
+
@handler.adapter Faraday.default_adapter
|
19
|
+
end
|
20
|
+
|
21
|
+
#### ApiClient::Connection::Abstract#get
|
22
|
+
# Performs a GET request
|
23
|
+
# Accepts three parameters:
|
24
|
+
#
|
25
|
+
# * path - the path the request should go to
|
26
|
+
# * data - (optional) the query, passed as a hash and converted into query params
|
27
|
+
# * headers - (optional) headers sent along with the request
|
28
|
+
#
|
29
|
+
def get(path, data = {}, headers = {})
|
30
|
+
exec_request(:get, path, data, headers)
|
31
|
+
end
|
32
|
+
|
33
|
+
#### ApiClient::Connection::Abstract#post
|
34
|
+
# Performs a POST request
|
35
|
+
# Accepts three parameters:
|
36
|
+
#
|
37
|
+
# * path - the path request should go to
|
38
|
+
# * data - (optional) data sent in the request
|
39
|
+
# * headers - (optional) headers sent along in the request
|
40
|
+
#
|
41
|
+
# This method automatically adds the application token header
|
42
|
+
def post(path, data = {}, headers = {})
|
43
|
+
exec_request(:post, path, data, headers)
|
44
|
+
end
|
45
|
+
|
46
|
+
#### ApiClient::Connection::Abstract#patch
|
47
|
+
# Performs a PATCH request
|
48
|
+
# Accepts three parameters:
|
49
|
+
#
|
50
|
+
# * path - the path request should go to
|
51
|
+
# * data - (optional) data sent in the request
|
52
|
+
# * headers - (optional) headers sent along in the request
|
53
|
+
#
|
54
|
+
# This method automatically adds the application token header
|
55
|
+
def patch(path, data = {}, headers = {})
|
56
|
+
exec_request(:patch, path, data, headers)
|
57
|
+
end
|
58
|
+
|
59
|
+
#### ApiClient::Connection::Abstract#put
|
60
|
+
# Performs a PUT request
|
61
|
+
# Accepts three parameters:
|
62
|
+
#
|
63
|
+
# * path - the path request should go to
|
64
|
+
# * data - (optional) data sent in the request
|
65
|
+
# * headers - (optional) headers sent along in the request
|
66
|
+
#
|
67
|
+
# This method automatically adds the application token header
|
68
|
+
def put(path, data = {}, headers = {})
|
69
|
+
exec_request(:put, path, data, headers)
|
70
|
+
end
|
71
|
+
|
72
|
+
#### FS::Connection#delete
|
73
|
+
# Performs a DELETE request
|
74
|
+
# Accepts three parameters:
|
75
|
+
#
|
76
|
+
# * path - the path request should go to
|
77
|
+
# * data - (optional) the query, passed as a hash and converted into query params
|
78
|
+
# * headers - (optional) headers sent along in the request
|
79
|
+
#
|
80
|
+
# This method automatically adds the application token header
|
81
|
+
def delete(path, data = {}, headers = {})
|
82
|
+
exec_request(:delete, path, data, headers)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def exec_request(method, path, data, headers)
|
88
|
+
response = @handler.send(method, path, data, headers)
|
89
|
+
request = { :method => method, :path => path, :data => data}
|
90
|
+
handle_response(request, response)
|
91
|
+
rescue Faraday::ConnectionFailed => e
|
92
|
+
raise ApiClient::Errors::ConnectionFailed.new(e.message, request, response)
|
93
|
+
end
|
94
|
+
|
95
|
+
def handle_response(request, response)
|
96
|
+
raise ApiClient::Errors::ConnectionFailed.new(nil, request, response) unless response
|
97
|
+
case response.status
|
98
|
+
when 401
|
99
|
+
raise ApiClient::Errors::Unauthorized.new(nil, request, response)
|
100
|
+
when 403
|
101
|
+
raise ApiClient::Errors::Forbidden.new(nil, request, response)
|
102
|
+
when 404
|
103
|
+
raise ApiClient::Errors::NotFound.new(nil, request, response)
|
104
|
+
when 400
|
105
|
+
raise ApiClient::Errors::BadRequest.new(nil, request, response)
|
106
|
+
when 406
|
107
|
+
raise ApiClient::Errors::Unsupported.new(nil, request, response)
|
108
|
+
when 409
|
109
|
+
raise ApiClient::Errors::Conflict.new(nil, request, response)
|
110
|
+
when 410
|
111
|
+
raise ApiClient::Errors::Gone.new(nil, request, response)
|
112
|
+
when 412
|
113
|
+
raise ApiClient::Errors::PreconditionFailed.new(nil, request, response)
|
114
|
+
when 422
|
115
|
+
raise ApiClient::Errors::UnprocessableEntity.new(response.body, request, response)
|
116
|
+
when 423
|
117
|
+
raise ApiClient::Errors::Locked.new(response.body, request, response)
|
118
|
+
when 429
|
119
|
+
raise ApiClient::Errors::TooManyRequests.new(response.body, request, response)
|
120
|
+
when 300..399
|
121
|
+
raise ApiClient::Errors::Redirect.new(response['Location'], request, response)
|
122
|
+
when 500..599
|
123
|
+
raise ApiClient::Errors::ServerError.new(nil, request, response)
|
124
|
+
else
|
125
|
+
response
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Exactly like Basic, but uses JSON encoding for request body
|
2
|
+
# if applicable
|
3
|
+
module ApiClient
|
4
|
+
module Connection
|
5
|
+
class Json < Basic
|
6
|
+
def finalize_handler
|
7
|
+
@handler.use Middlewares::Request::Logger, ApiClient.logger if ApiClient.logger
|
8
|
+
@handler.use Middlewares::Request::Json
|
9
|
+
@handler.use Faraday::Request::UrlEncoded
|
10
|
+
@handler.adapter Faraday.default_adapter
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class ApiClient::Connection::Middlewares::Request::Json < Faraday::Middleware
|
2
|
+
CONTENT_TYPE = "Content-Type".freeze
|
3
|
+
|
4
|
+
class << self
|
5
|
+
attr_accessor :mime_type
|
6
|
+
end
|
7
|
+
self.mime_type = "application/json".freeze
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
match_content_type(env) do |data|
|
11
|
+
params = Faraday::Utils::ParamsHash[data]
|
12
|
+
env[:body] = MultiJson.dump(params)
|
13
|
+
end
|
14
|
+
@app.call env
|
15
|
+
end
|
16
|
+
|
17
|
+
def match_content_type(env)
|
18
|
+
if process_request?(env)
|
19
|
+
env[:request_headers][CONTENT_TYPE] ||= self.class.mime_type
|
20
|
+
yield env[:body] unless env[:body].respond_to?(:to_str)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_request?(env)
|
25
|
+
type = request_type(env)
|
26
|
+
env[:body] and (type.empty? or type == self.class.mime_type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def request_type(env)
|
30
|
+
type = env[:request_headers][CONTENT_TYPE].to_s
|
31
|
+
type = type.split(";", 2).first if type.index(";")
|
32
|
+
type
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
class ApiClient::Connection::Middlewares::Request::Logger < Faraday::Middleware
|
4
|
+
def call(env)
|
5
|
+
debug_lines = []
|
6
|
+
should_log_details = @logger.level <= ::Logger::DEBUG
|
7
|
+
|
8
|
+
gather_request_debug_lines(env, debug_lines) if should_log_details
|
9
|
+
|
10
|
+
start = CurrentTimestamp.milis
|
11
|
+
response = @app.call(env)
|
12
|
+
taken_sec = (CurrentTimestamp.milis - start) / 1000.0
|
13
|
+
|
14
|
+
gather_response_debug_lines(response, taken_sec, debug_lines) if response && should_log_details
|
15
|
+
|
16
|
+
if should_log_details
|
17
|
+
debug_lines.each { |line| line.encode!("UTF-8", invalid: :replace, undef: :replace) }
|
18
|
+
@logger.debug { debug_lines.join("\n") }
|
19
|
+
else
|
20
|
+
@logger.info { "#{env[:method].to_s.upcase} #{env[:url]}: #{"%.4f" % taken_sec} seconds" }
|
21
|
+
end
|
22
|
+
|
23
|
+
response
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(app, logger = nil)
|
27
|
+
@logger = logger || ::Logger.new(STDOUT)
|
28
|
+
@app = app
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def gather_request_debug_lines(env, debug_lines)
|
34
|
+
debug_lines << "> #{env[:method].to_s.upcase} #{env[:url]}"
|
35
|
+
env[:request_headers].each { |k, v| debug_lines << "> #{k}: #{v}" }
|
36
|
+
debug_lines << "> "
|
37
|
+
debug_lines << "> #{env[:body]}\n> " if env[:body] && env[:body] != ""
|
38
|
+
debug_lines
|
39
|
+
end
|
40
|
+
|
41
|
+
def gather_response_debug_lines(response, taken_sec, debug_lines)
|
42
|
+
debug_lines << "< responded in #{"%.4f" % taken_sec} seconds with HTTP #{response.status}"
|
43
|
+
response.headers.each { |k, v| debug_lines << "< #{k}: #{v}" }
|
44
|
+
debug_lines << "< "
|
45
|
+
debug_lines << "< #{response.body}\n> " if response.body && response.body != ""
|
46
|
+
debug_lines
|
47
|
+
end
|
48
|
+
|
49
|
+
class CurrentTimestamp
|
50
|
+
class << self
|
51
|
+
version = RUBY_VERSION.split('.').map(&:to_i)
|
52
|
+
|
53
|
+
if (version[0] < 2) || (version[0] == 2 && version[1] < 2)
|
54
|
+
def milis
|
55
|
+
(Time.now.to_f * 1000).to_i
|
56
|
+
end
|
57
|
+
else
|
58
|
+
def milis
|
59
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Borrowed from https://github.com/pengwynn/faraday_middleware/blob/master/lib/faraday/request/oauth.rb
|
2
|
+
class ApiClient::Connection::Middlewares::Request::OAuth < Faraday::Middleware
|
3
|
+
|
4
|
+
dependency 'simple_oauth'
|
5
|
+
|
6
|
+
def call(env)
|
7
|
+
params = env[:body] || {}
|
8
|
+
signature_params = params.reject{ |k,v| v.respond_to?(:content_type) }
|
9
|
+
|
10
|
+
header = SimpleOAuth::Header.new(env[:method], env[:url], signature_params, @options || {})
|
11
|
+
|
12
|
+
env[:request_headers]['Authorization'] = header.to_s
|
13
|
+
env[:request_headers]['User-Agent'] = "ApiClient gem v#{ApiClient::VERSION}"
|
14
|
+
|
15
|
+
@app.call(env)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(app, options = {})
|
19
|
+
@app, @options = app, options
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ApiClient
|
2
|
+
|
3
|
+
module Connection
|
4
|
+
|
5
|
+
class Oauth < Basic
|
6
|
+
|
7
|
+
def finalize_handler
|
8
|
+
@handler.use Middlewares::Request::Logger, ApiClient.logger if ApiClient.logger
|
9
|
+
@handler.use Middlewares::Request::OAuth, @options[:oauth]
|
10
|
+
@handler.use Faraday::Request::UrlEncoded
|
11
|
+
@handler.adapter Faraday.default_adapter
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ApiClient
|
2
|
+
|
3
|
+
module Errors
|
4
|
+
class ApiClientError < StandardError
|
5
|
+
def initialize(message = nil, request = nil, response = nil)
|
6
|
+
message ||= "Status code: #{response.status}" if response
|
7
|
+
super(message)
|
8
|
+
@request = request
|
9
|
+
@response = response
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :request, :response
|
13
|
+
end
|
14
|
+
|
15
|
+
class ConnectionFailed < ApiClientError; end
|
16
|
+
class Config < ApiClientError; end
|
17
|
+
class Unauthorized < ApiClientError; end
|
18
|
+
class Forbidden < ApiClientError; end
|
19
|
+
class NotFound < ApiClientError; end
|
20
|
+
class Redirect < ApiClientError; end
|
21
|
+
class BadRequest < ApiClientError; end
|
22
|
+
class Unsupported < ApiClientError; end
|
23
|
+
class Conflict < ApiClientError; end
|
24
|
+
class Gone < ApiClientError; end
|
25
|
+
class ServerError < ApiClientError; end
|
26
|
+
class UnprocessableEntity < ApiClientError; end
|
27
|
+
class PreconditionFailed < ApiClientError; end
|
28
|
+
class Locked < ApiClientError; end
|
29
|
+
class TooManyRequests < ApiClientError; end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ApiClient
|
2
|
+
|
3
|
+
module Mixins
|
4
|
+
|
5
|
+
module Configuration
|
6
|
+
|
7
|
+
def dsl_accessor(*names)
|
8
|
+
options = names.last.is_a?(Hash) ? names.pop : {}
|
9
|
+
names.each do |name|
|
10
|
+
returns = options[:return_self] ? "self" : "@#{name}"
|
11
|
+
class_eval <<-STR
|
12
|
+
def #{name}(value = nil)
|
13
|
+
value.nil? ? @#{name} : @#{name} = value
|
14
|
+
#{returns}
|
15
|
+
end
|
16
|
+
STR
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ApiClient
|
2
|
+
|
3
|
+
module Mixins
|
4
|
+
|
5
|
+
module ConnectionHooks
|
6
|
+
|
7
|
+
attr_accessor :connection_hooks
|
8
|
+
|
9
|
+
def connection(&block)
|
10
|
+
@connection_hooks ||= []
|
11
|
+
@connection_hooks.push(block) if block
|
12
|
+
@connection_hooks
|
13
|
+
end
|
14
|
+
|
15
|
+
def connection_hooks
|
16
|
+
@connection_hooks || []
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ApiClient
|
2
|
+
|
3
|
+
module Mixins
|
4
|
+
|
5
|
+
module Delegation
|
6
|
+
|
7
|
+
def delegate(*methods)
|
8
|
+
hash = methods.pop
|
9
|
+
to = hash[:to]
|
10
|
+
methods.each do |method|
|
11
|
+
class_eval <<-STR
|
12
|
+
def #{method}(*args, &block)
|
13
|
+
#{to}.#{method}(*args, &block)
|
14
|
+
end
|
15
|
+
STR
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ApiClient
|
2
|
+
|
3
|
+
module Mixins
|
4
|
+
|
5
|
+
module Inheritance
|
6
|
+
|
7
|
+
def inherited(subclass)
|
8
|
+
subclass.default_scopes = self.default_scopes.dup
|
9
|
+
subclass.connection_hooks = self.connection_hooks.dup
|
10
|
+
|
11
|
+
subclass.namespace self.namespace
|
12
|
+
subclass.format self.format
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ApiClient
|
2
|
+
module Mixins
|
3
|
+
module Instantiation
|
4
|
+
def self.extended(base)
|
5
|
+
base.instance_eval do
|
6
|
+
attr_accessor :original_scope
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_one(hash)
|
11
|
+
instance = self.new self.namespace ? hash[namespace] : hash
|
12
|
+
instance.original_scope = self.scope.clone_only_headers
|
13
|
+
instance
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_many(array)
|
17
|
+
array.collect { |one| build_one(one) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def build(result_or_array)
|
21
|
+
if result_or_array.is_a?(Array)
|
22
|
+
build_many result_or_array
|
23
|
+
else
|
24
|
+
build_one result_or_array
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ApiClient
|
2
|
+
|
3
|
+
module Mixins
|
4
|
+
|
5
|
+
module Scoping
|
6
|
+
|
7
|
+
attr_accessor :default_scopes
|
8
|
+
|
9
|
+
# Default scoping
|
10
|
+
def always(&block)
|
11
|
+
default_scopes.push(block) if block
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_scopes
|
15
|
+
@default_scopes || []
|
16
|
+
end
|
17
|
+
|
18
|
+
# Scoping
|
19
|
+
def scope(options = {})
|
20
|
+
scope_in_thread || Scope.new(self).params(options)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Allow wrapping singleton methods in a scope
|
24
|
+
# Store the handler in a thread-local variable for thread safety
|
25
|
+
def scoped(scope)
|
26
|
+
Thread.current[scope_thread_attribute_name] ||= []
|
27
|
+
Thread.current[scope_thread_attribute_name].push scope
|
28
|
+
begin
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
Thread.current[scope_thread_attribute_name] = nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def scope_thread_attribute_name
|
36
|
+
"#{self.name}_scope"
|
37
|
+
end
|
38
|
+
|
39
|
+
def scope_in_thread
|
40
|
+
if found = Thread.current[scope_thread_attribute_name]
|
41
|
+
found.last
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|