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.
@@ -1,8 +1,8 @@
1
1
  module ApiRecipes
2
2
 
3
- class EndpointNameClashError < Exception
3
+ class ApiNameClashError < Exception
4
4
  def initialize(object, endpoint_name)
5
- message = "#{object.class} already defines a method called '#{endpoint_name}'. Tip: change endpoint name"
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
- attr_reader :resource, :route, :attribute
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
- def initialize(message = nil, resource = nil, route = nil, attribute = nil)
21
- @resource = resource; @route = route; @attribute = attribute
22
- if message
23
- # Nothing to do
24
- elsif @route && @attribute
25
- message = "route '#{@resource}.#{@route}' requires '#{@attribute}' attribute but this was not given"
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
- attr_reader :resource, :route, :expected_code, :response_code, :response_body
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 EndpointConfigIsNotAnHash < Exception
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
- attr_reader :resource, :route
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
- def initialize(resource, route)
77
- @resource = resource; @route = route
78
- message = "can't define route '#{@route}' method in resource '#{@resource}' because method '#{@route}' already exists"
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
@@ -1,5 +1,22 @@
1
1
  module ApiRecipes
2
- class Response < HTTP::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
@@ -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
- base_path: '',
11
- api_version: '',
9
+ base_url: nil,
12
10
  timeout: 3,
13
- on_nok_code: :raise,
14
- routes: {}
11
+ on_bad_code: 'raise',
12
+ endpoints: {}
15
13
  }
16
14
 
17
15
  DEFAULT_ROUTE_ATTRIBUTES = {
18
- method: :get
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)
@@ -1,3 +1,3 @@
1
1
  module ApiRecipes
2
- VERSION = '1.0.0'
2
+ VERSION = '2.5.0'.freeze
3
3
  end
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: 1.0.0
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: 2019-07-09 00:00:00.000000000 Z
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.1.1
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.1.1
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
- - ".gitignore"
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.2.0
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.9
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
@@ -1,16 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- *.orig
11
- .idea
12
- .rvmrc
13
- .ruby-version
14
- .ruby-gemset
15
- .DS_Store
16
- *.gem
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --format documentation
2
- --color
@@ -1,10 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1.0
6
- - 2.2.0
7
- - 2.2.3
8
- notifications:
9
- email: false
10
- bundler_args: --without development
@@ -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