api_recipes 1.0.0 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,15 @@ 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}"
43
+ def initialize(path, expected_code = nil, response_code = nil, response_body = nil, message: nil)
44
+ unless message
45
+ message = "response code for request on route '#{path}' has returned '#{response_code}', but '#{expected_code}' was expected\n\nResponse body:\n #{response_body}"
47
46
  end
48
47
  super(message)
49
48
  end
50
49
  end
51
50
 
52
- class EndpointConfigIsNotAnHash < Exception
51
+ class ApiConfigIsNotAnHash < Exception
53
52
  def initialize(endpoint)
54
53
  message = "provided config for endpoint '#{endpoint}' must be an Hash"
55
54
  super(message)
@@ -71,11 +70,15 @@ module ApiRecipes
71
70
  end
72
71
 
73
72
  class RouteNameClashWithExistentMethod < Exception
74
- attr_reader :resource, :route
73
+ def initialize(endpoint_name, route_name)
74
+ message = "can't define route '#{route_name}' method in endpoint '#{endpoint_name}' because method '#{route_name}' has already been defined"
75
+ super(message)
76
+ end
77
+ end
75
78
 
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"
79
+ class NoRouteExists < Exception
80
+ def initialize(endpoint_name)
81
+ message = "no route defined on #{endpoint_name}"
79
82
  super(message)
80
83
  end
81
84
  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,165 @@
1
+ module ApiRecipes
2
+ class Route
3
+
4
+ attr_reader :request, :response, :url
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
+ @url = 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, @url, 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_url_from_path
45
+ attrs = {
46
+ scheme: settings[:protocol],
47
+ host: settings[:host],
48
+ port: port,
49
+ path: path
50
+ }
51
+ URI::Generic.build2 attrs
52
+ end
53
+
54
+ def check_response_code
55
+ ok_code = false
56
+ code = @response.code
57
+ message = nil
58
+
59
+ verify_with = attributes[:verify_with]
60
+ if verify_with && @api.object.respond_to?(verify_with, true )
61
+ ok_code = @api.object.send verify_with, @response
62
+ message = "response for request on route '#{path}' was not valid. Verified with #{@api.object}##{verify_with}.\n\nResponse body:\n #{@response.body}"
63
+ else
64
+ # If :ok_code property is present, check the response code
65
+ if expected_code = attributes[:ok_code]
66
+ # If the code does not match, apply the requested strategy
67
+ ok_code = true if code == expected_code
68
+ else
69
+ # Default: 200 <= OK < 300
70
+ ok_code = true if @response.status.success?
71
+ expected_code = '200 <= CODE < 300'
72
+ end
73
+ end
74
+
75
+ unless ok_code
76
+ case attributes[:on_bad_code].to_s
77
+ when 'ignore'
78
+ when 'raise'
79
+ raise ResponseCodeNotAsExpected.new(path, expected_code, code, @response.body, message: message)
80
+ end
81
+ end
82
+ end
83
+
84
+ def extract_headers
85
+ settings[:default_headers] || {}
86
+ end
87
+
88
+ def timeout
89
+ attributes.fetch(:timeout, ApiRecipes::Settings::GLOBAL_TIMEOUT)
90
+ end
91
+
92
+ def port
93
+ settings[:port] || case settings[:protocol]
94
+ when 'http'
95
+ 80
96
+ when 'https'
97
+ 443
98
+ end
99
+ end
100
+
101
+ def prepare_request
102
+ @url = build_url_from_path
103
+ puts @url if ApiRecipes.configuration.print_urls
104
+
105
+ @request = request_with_auth
106
+ end
107
+
108
+ def request_params=(params)
109
+ unless params.is_a? Hash
110
+ raise ArgumentError, 'provided params must be an Hash'
111
+ end
112
+ # Merge route attributes with defaults and deep clone route attributes
113
+ @request_params = params
114
+ end
115
+
116
+ def request_with_auth
117
+ req = HTTP
118
+ req = req.headers(extract_headers)
119
+ .timeout(timeout)
120
+
121
+ basic_auth = @api.basic_auth
122
+ if basic_auth
123
+ req = req.basic_auth basic_auth
124
+ end
125
+ authorization = @api.authorization
126
+ if authorization
127
+ req = req.auth authorization
128
+ end
129
+ req
130
+ end
131
+
132
+ def settings
133
+ @api.configs
134
+ end
135
+
136
+ def try_to_fill(object, data)
137
+ case data
138
+ when Hash
139
+ res = fill_object_with object, data
140
+ when Array
141
+ res = []
142
+ data.each do |element|
143
+ res << fill_object_with(object.new, element)
144
+ end
145
+ end
146
+
147
+ res
148
+ end
149
+
150
+ def fill_object_with(object, data)
151
+ data.each do |key, value|
152
+ begin
153
+ object.send "#{key}=", value
154
+ rescue
155
+ end
156
+ end
157
+
158
+ object
159
+ end
160
+
161
+ def http_verb
162
+ attributes[:verb]
163
+ end
164
+ end
165
+ end
@@ -1,21 +1,26 @@
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: {},
13
+ verify_with: nil
15
14
  }
16
15
 
17
16
  DEFAULT_ROUTE_ATTRIBUTES = {
18
- method: :get
17
+ verb: :get,
18
+ route: 'yes',
19
+ path: nil,
20
+ ok_code: nil,
21
+ timeout: DEFAULT[:timeout],
22
+ on_bad_code: DEFAULT[:on_bad_code],
23
+ verify_with: DEFAULT[:verify_with]
19
24
  }
20
25
 
21
26
  AVAILABLE_PARAMS_ENCODINGS = %w(form params json body)
@@ -1,3 +1,3 @@
1
1
  module ApiRecipes
2
- VERSION = '1.0.0'
2
+ VERSION = '2.7.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.7.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-11-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,23 @@ 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
+ rubygems_version: 3.0.8
66
+ signing_key:
87
67
  specification_version: 4
88
68
  summary: Consume HTTP APIs with style
89
69
  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