api_recipes 1.0.0 → 2.5.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/{LICENSE.txt → MIT-LICENSE} +0 -0
- data/README.md +2 -2
- data/lib/api_recipes.rb +60 -59
- data/lib/api_recipes/api.rb +69 -0
- data/lib/api_recipes/configuration.rb +8 -8
- data/lib/api_recipes/endpoint.rb +105 -30
- data/lib/api_recipes/exceptions.rb +24 -23
- data/lib/api_recipes/response.rb +18 -1
- data/lib/api_recipes/route.rb +158 -0
- data/lib/api_recipes/settings.rb +9 -6
- data/lib/api_recipes/version.rb +1 -1
- metadata +15 -34
- data/.gitignore +0 -16
- data/.rspec +0 -2
- data/.travis.yml +0 -10
- data/CHANGELOG.md +0 -15
- data/CODE_OF_CONDUCT.md +0 -13
- data/Gemfile +0 -12
- data/Guardfile +0 -14
- data/Rakefile +0 -6
- data/api_recipes.gemspec +0 -22
- data/api_recipes.png +0 -0
- data/bin/console +0 -14
- data/bin/setup +0 -7
- data/examples/authorization.rb +0 -42
- data/examples/authorization_with_default_headers.rb +0 -41
- data/examples/basic_auth.rb +0 -32
- data/examples/config/apis.yml +0 -43
- data/examples/custom_configs.rb +0 -51
- data/examples/delete_me.rb +0 -21
- data/examples/multiple_endpoints.rb +0 -54
- data/examples/simple_usage.rb +0 -38
- data/lib/api_recipes/resource.rb +0 -192
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module ApiRecipes
|
|
2
2
|
|
|
3
|
-
class
|
|
3
|
+
class ApiNameClashError < Exception
|
|
4
4
|
def initialize(object, endpoint_name)
|
|
5
|
-
message = "#{object.class} already defines a method called '#{endpoint_name}'. Tip: change
|
|
5
|
+
message = "#{object.class} already defines a method called '#{endpoint_name}'. Tip: change api name"
|
|
6
6
|
super(message)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
@@ -15,14 +15,18 @@ module ApiRecipes
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
class MissingRouteAttribute < Exception
|
|
18
|
-
|
|
18
|
+
def initialize(endpoint = nil, route = nil, attribute = nil)
|
|
19
|
+
message = "route '#{endpoint}.#{route}' requires '#{attribute}' attribute but this was not provided"
|
|
20
|
+
super(message)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
message = "route '#{
|
|
24
|
+
class PathParamsMismatch < Exception
|
|
25
|
+
def initialize(path, expected_params, provided_params)
|
|
26
|
+
if expected_params.size == 0
|
|
27
|
+
message = "route '#{path}' requires NO PARAMS but #{provided_params} were provided"
|
|
28
|
+
else
|
|
29
|
+
message = "route '#{path}' requires params #{expected_params} but #{provided_params} were provided"
|
|
26
30
|
end
|
|
27
31
|
super(message)
|
|
28
32
|
end
|
|
@@ -36,20 +40,13 @@ module ApiRecipes
|
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
class ResponseCodeNotAsExpected < Exception
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def initialize(message = nil, resource = nil, route = nil, expected_code = nil, response_code = nil, response_body = nil)
|
|
42
|
-
@resource = resource; @route = route; @expected_code = expected_code; @response_code = response_code; @response_body = response_body
|
|
43
|
-
if message
|
|
44
|
-
# Nothing to do
|
|
45
|
-
else
|
|
46
|
-
message = "response code for request on route '#{@resource}.#{@route}' has returned #{@response_code}, but #{@expected_code} was expected. Reason: #{@response_body}"
|
|
47
|
-
end
|
|
43
|
+
def initialize(path, expected_code = nil, response_code = nil, response_body = nil)
|
|
44
|
+
message = "response code for request on route '#{path}' has returned #{response_code}, but #{expected_code} was expected\n\nResponse body:\n #{response_body}"
|
|
48
45
|
super(message)
|
|
49
46
|
end
|
|
50
47
|
end
|
|
51
48
|
|
|
52
|
-
class
|
|
49
|
+
class ApiConfigIsNotAnHash < Exception
|
|
53
50
|
def initialize(endpoint)
|
|
54
51
|
message = "provided config for endpoint '#{endpoint}' must be an Hash"
|
|
55
52
|
super(message)
|
|
@@ -71,11 +68,15 @@ module ApiRecipes
|
|
|
71
68
|
end
|
|
72
69
|
|
|
73
70
|
class RouteNameClashWithExistentMethod < Exception
|
|
74
|
-
|
|
71
|
+
def initialize(endpoint_name, route_name)
|
|
72
|
+
message = "can't define route '#{route_name}' method in endpoint '#{endpoint_name}' because method '#{route_name}' has already been defined"
|
|
73
|
+
super(message)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
message = "
|
|
77
|
+
class NoRouteExists < Exception
|
|
78
|
+
def initialize(endpoint_name)
|
|
79
|
+
message = "no route defined on #{endpoint_name}"
|
|
79
80
|
super(message)
|
|
80
81
|
end
|
|
81
82
|
end
|
data/lib/api_recipes/response.rb
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
module ApiRecipes
|
|
2
|
-
class Response
|
|
2
|
+
class Response
|
|
3
3
|
|
|
4
|
+
attr_reader :original_response
|
|
5
|
+
|
|
6
|
+
def initialize(response, attributes = {})
|
|
7
|
+
@original_response = response
|
|
8
|
+
@attributes = attributes
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def data
|
|
12
|
+
return @data unless @data.nil?
|
|
13
|
+
|
|
14
|
+
@data = @original_response.parse
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Forward method calls to 'original' Response class
|
|
18
|
+
def method_missing(symbol, *args, &block)
|
|
19
|
+
@original_response.send symbol,* args, &block
|
|
20
|
+
end
|
|
4
21
|
end
|
|
5
22
|
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
module ApiRecipes
|
|
2
|
+
class Route
|
|
3
|
+
|
|
4
|
+
attr_reader :request, :response
|
|
5
|
+
attr_accessor :request_params, :attributes, :path
|
|
6
|
+
|
|
7
|
+
def initialize(api: nil, endpoint: nil, path: nil, attributes: {}, req_pars: {})
|
|
8
|
+
@api = api
|
|
9
|
+
@endpoint = endpoint
|
|
10
|
+
@path = path.to_s
|
|
11
|
+
@attributes = attributes
|
|
12
|
+
self.request_params = req_pars
|
|
13
|
+
@uri = nil
|
|
14
|
+
|
|
15
|
+
prepare_request
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def fill(object)
|
|
19
|
+
data = @response.parse
|
|
20
|
+
if block_given?
|
|
21
|
+
tap do
|
|
22
|
+
try_to_fill object, data
|
|
23
|
+
yield object, data, @response.status
|
|
24
|
+
end
|
|
25
|
+
else
|
|
26
|
+
try_to_fill object, data
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def start_request(&block)
|
|
31
|
+
original_response = @request.send http_verb, @uri, request_params
|
|
32
|
+
@response = Response.new original_response, attributes
|
|
33
|
+
check_response_code
|
|
34
|
+
|
|
35
|
+
if block_given?
|
|
36
|
+
tap { block.call @response }
|
|
37
|
+
else
|
|
38
|
+
response
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def build_uri_from_path
|
|
45
|
+
attrs = {
|
|
46
|
+
scheme: settings[:protocol],
|
|
47
|
+
host: settings[:host],
|
|
48
|
+
port: port,
|
|
49
|
+
path: path
|
|
50
|
+
}
|
|
51
|
+
URI::Generic.build attrs
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def check_response_code
|
|
55
|
+
# If :ok_code property is present, check the response code
|
|
56
|
+
ok_code = false
|
|
57
|
+
code = @response.code
|
|
58
|
+
if expected_code = attributes[:ok_code]
|
|
59
|
+
# If the code does not match, apply the requested strategy
|
|
60
|
+
ok_code = true if code == expected_code
|
|
61
|
+
else
|
|
62
|
+
# Default: 200 <= OK < 300
|
|
63
|
+
ok_code = true if (code >= 200 && code < 300)
|
|
64
|
+
expected_code = '200 <= CODE < 300'
|
|
65
|
+
end
|
|
66
|
+
unless ok_code
|
|
67
|
+
case attributes[:on_bad_code].to_s
|
|
68
|
+
when 'ignore'
|
|
69
|
+
when 'raise'
|
|
70
|
+
raise ResponseCodeNotAsExpected.new(path, expected_code, code, @response.body)
|
|
71
|
+
when 'return_false'
|
|
72
|
+
return false
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def extract_headers
|
|
78
|
+
settings[:default_headers] || {}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def timeout
|
|
82
|
+
attributes.fetch(:timeout, ApiRecipes::Settings::GLOBAL_TIMEOUT)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def port
|
|
86
|
+
settings[:port] || case settings[:protocol]
|
|
87
|
+
when 'http'
|
|
88
|
+
80
|
|
89
|
+
when 'https'
|
|
90
|
+
443
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def prepare_request
|
|
95
|
+
@uri = build_uri_from_path
|
|
96
|
+
puts @uri if ApiRecipes.print_urls
|
|
97
|
+
|
|
98
|
+
@request = request_with_auth
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def request_params=(params)
|
|
102
|
+
unless params.is_a? Hash
|
|
103
|
+
raise ArgumentError, 'provided params must be an Hash'
|
|
104
|
+
end
|
|
105
|
+
# Merge route attributes with defaults and deep clone route attributes
|
|
106
|
+
@request_params = params
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def request_with_auth
|
|
110
|
+
req = HTTP
|
|
111
|
+
req = req.headers(extract_headers)
|
|
112
|
+
.timeout(timeout)
|
|
113
|
+
|
|
114
|
+
basic_auth = @api.basic_auth
|
|
115
|
+
if basic_auth
|
|
116
|
+
req = req.basic_auth basic_auth
|
|
117
|
+
end
|
|
118
|
+
authorization = @api.authorization
|
|
119
|
+
if authorization
|
|
120
|
+
req = req.auth authorization
|
|
121
|
+
end
|
|
122
|
+
req
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def settings
|
|
126
|
+
@api.configs
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def try_to_fill(object, data)
|
|
130
|
+
case data
|
|
131
|
+
when Hash
|
|
132
|
+
res = fill_object_with object, data
|
|
133
|
+
when Array
|
|
134
|
+
res = []
|
|
135
|
+
data.each do |element|
|
|
136
|
+
res << fill_object_with(object.new, element)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
res
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def fill_object_with(object, data)
|
|
144
|
+
data.each do |key, value|
|
|
145
|
+
begin
|
|
146
|
+
object.send "#{key}=", value
|
|
147
|
+
rescue
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
object
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def http_verb
|
|
155
|
+
attributes[:verb]
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
data/lib/api_recipes/settings.rb
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
module ApiRecipes
|
|
2
2
|
module Settings
|
|
3
3
|
GLOBAL_TIMEOUT = 1
|
|
4
|
-
FAIL_OPTIONS = [:return, :raise, :return_false]
|
|
5
4
|
|
|
6
5
|
DEFAULT = {
|
|
7
6
|
protocol: 'https',
|
|
8
7
|
host: 'localhost',
|
|
9
8
|
port: nil,
|
|
10
|
-
|
|
11
|
-
api_version: '',
|
|
9
|
+
base_url: nil,
|
|
12
10
|
timeout: 3,
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
on_bad_code: 'raise',
|
|
12
|
+
endpoints: {}
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
DEFAULT_ROUTE_ATTRIBUTES = {
|
|
18
|
-
|
|
16
|
+
verb: :get,
|
|
17
|
+
route: 'yes',
|
|
18
|
+
path: nil,
|
|
19
|
+
ok_code: nil,
|
|
20
|
+
timeout: DEFAULT[:timeout],
|
|
21
|
+
on_bad_code: DEFAULT[:on_bad_code]
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
AVAILABLE_PARAMS_ENCODINGS = %w(form params json body)
|
data/lib/api_recipes/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: api_recipes
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alessandro Verlato
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-09-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: http
|
|
@@ -16,49 +16,30 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 4.
|
|
19
|
+
version: 4.4.1
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 4.
|
|
27
|
-
description:
|
|
26
|
+
version: 4.4.1
|
|
27
|
+
description:
|
|
28
28
|
email:
|
|
29
29
|
- averlato@gmail.com
|
|
30
30
|
executables: []
|
|
31
31
|
extensions: []
|
|
32
32
|
extra_rdoc_files: []
|
|
33
33
|
files:
|
|
34
|
-
-
|
|
35
|
-
- ".rspec"
|
|
36
|
-
- ".travis.yml"
|
|
37
|
-
- CHANGELOG.md
|
|
38
|
-
- CODE_OF_CONDUCT.md
|
|
39
|
-
- Gemfile
|
|
40
|
-
- Guardfile
|
|
41
|
-
- LICENSE.txt
|
|
34
|
+
- MIT-LICENSE
|
|
42
35
|
- README.md
|
|
43
|
-
- Rakefile
|
|
44
|
-
- api_recipes.gemspec
|
|
45
|
-
- api_recipes.png
|
|
46
|
-
- bin/console
|
|
47
|
-
- bin/setup
|
|
48
|
-
- examples/authorization.rb
|
|
49
|
-
- examples/authorization_with_default_headers.rb
|
|
50
|
-
- examples/basic_auth.rb
|
|
51
|
-
- examples/config/apis.yml
|
|
52
|
-
- examples/custom_configs.rb
|
|
53
|
-
- examples/delete_me.rb
|
|
54
|
-
- examples/multiple_endpoints.rb
|
|
55
|
-
- examples/simple_usage.rb
|
|
56
36
|
- lib/api_recipes.rb
|
|
37
|
+
- lib/api_recipes/api.rb
|
|
57
38
|
- lib/api_recipes/configuration.rb
|
|
58
39
|
- lib/api_recipes/endpoint.rb
|
|
59
40
|
- lib/api_recipes/exceptions.rb
|
|
60
|
-
- lib/api_recipes/resource.rb
|
|
61
41
|
- lib/api_recipes/response.rb
|
|
42
|
+
- lib/api_recipes/route.rb
|
|
62
43
|
- lib/api_recipes/settings.rb
|
|
63
44
|
- lib/api_recipes/utils.rb
|
|
64
45
|
- lib/api_recipes/version.rb
|
|
@@ -66,24 +47,24 @@ homepage: https://github.com/madAle/api_recipes
|
|
|
66
47
|
licenses:
|
|
67
48
|
- MIT
|
|
68
49
|
metadata: {}
|
|
69
|
-
post_install_message:
|
|
50
|
+
post_install_message:
|
|
70
51
|
rdoc_options: []
|
|
71
52
|
require_paths:
|
|
72
53
|
- lib
|
|
73
54
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
74
55
|
requirements:
|
|
75
|
-
- - "
|
|
56
|
+
- - ">="
|
|
76
57
|
- !ruby/object:Gem::Version
|
|
77
|
-
version: 2.
|
|
58
|
+
version: 2.5.0
|
|
78
59
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
60
|
requirements:
|
|
80
61
|
- - ">="
|
|
81
62
|
- !ruby/object:Gem::Version
|
|
82
63
|
version: '0'
|
|
83
64
|
requirements: []
|
|
84
|
-
rubyforge_project:
|
|
85
|
-
rubygems_version: 2.7.
|
|
86
|
-
signing_key:
|
|
65
|
+
rubyforge_project:
|
|
66
|
+
rubygems_version: 2.7.7
|
|
67
|
+
signing_key:
|
|
87
68
|
specification_version: 4
|
|
88
69
|
summary: Consume HTTP APIs with style
|
|
89
70
|
test_files: []
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.travis.yml
DELETED
data/CHANGELOG.md
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
## Version 0.6.0 - Default to HTTPS
|
|
2
|
-
|
|
3
|
-
**POSSIBLE BREAKING CHANGES**
|
|
4
|
-
|
|
5
|
-
Requests now default to HTTPS. If you still want to use (unsecure) HTTP you have to specify it in your configs through the dedicated `protocol` option e.g.
|
|
6
|
-
|
|
7
|
-
```yaml
|
|
8
|
-
github:
|
|
9
|
-
protocol: http
|
|
10
|
-
host: api.github.com
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Version 0.5.0 - Better Exceptions
|
|
14
|
-
|
|
15
|
-
Exceptions now include attributes that can be accessed during the `rescue` phase in order to execute specific actions
|