nxt_http_client 1.0.4 → 2.0.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
  SHA256:
3
- metadata.gz: f15083d70472500d85d5b14313db955407d41c0381253824df06d7b62408be81
4
- data.tar.gz: c8eac41c1f068d3cfb2892eadda053deb928e8bb1595cade76549d53e72db677
3
+ metadata.gz: 1a6f3d4b612e96bdbb4ca6eb96ad25572bfcccee632e140945770e643cc875a7
4
+ data.tar.gz: 983ce0f4bc00f4b3d6dec1956736714cf3bf2f367eedd3c6cbab4c1505c56ba5
5
5
  SHA512:
6
- metadata.gz: edaeb82434e63eaebe71ddfc93d029d91e468d800dc21b42825136e483ef4353f229df48737eb59f373bea51f5365c8d3ba6faea553b93ac4b4fa4f2eed5c376
7
- data.tar.gz: 141abc01af4c33257773c42e4d8f6ca82e59034e9ecdbd49adf837b395d0c345bbc493e0e718f2c779d1befeea4057c0ef9a1b5913315a25419349aeec1a8319
6
+ metadata.gz: a8b12f8c808a3b71f9d23a734ecc16022adb975a0530cad5e5251745aaf4cf818f781645eb2069f60aa900dbf72996dae4a744c7bd83e89b48d0b80f02c252a2
7
+ data.tar.gz: 1abe1f25bbceaa78ef1198c4e5b26a2b6c3091f53b06b6aa4a85a2182f19bd26895620ecb8584c8afd13a8aa919f94b83f1efcdc2b8db1d03a37df98e9105ee4
data/.circleci/config.yml CHANGED
@@ -1,57 +1,20 @@
1
- # Ruby CircleCI 2.0 configuration file
2
- #
3
- # Check https://circleci.com/docs/2.0/language-ruby/ for more details
4
- #
5
- version: 2
1
+ version: 2.1
2
+
3
+ orbs:
4
+ ruby: circleci/ruby@2.0.1
5
+
6
6
  jobs:
7
7
  build:
8
8
  docker:
9
- - image: circleci/ruby:2.7.4-node
10
- - image: circleci/redis:5.0.4
11
- environment:
12
- BUNDLER_VERSION: 2.3.6
9
+ - image: cimg/ruby:3.2.2-node
10
+ - image: cimg/redis:6.0.16
13
11
 
14
12
  working_directory: ~/repo
15
13
 
16
14
  steps:
17
15
  - checkout
18
16
 
19
- # Download and cache dependencies
20
- - restore_cache:
21
- keys:
22
- - v1-dependencies-{{ checksum "Gemfile.lock" }}
23
-
24
- - run: gem install bundler --version $BUNDLER_VERSION
25
-
26
- - run:
27
- name: install dependencies
28
- command: |
29
- bundle install --jobs=4 --retry=3 --path vendor/bundle
30
-
31
- - save_cache:
32
- paths:
33
- - ./vendor/bundle
34
- key: v1-dependencies-{{ checksum "Gemfile.lock" }}
35
-
36
- # run tests!
37
- - run:
38
- name: run tests
39
- command: |
40
- mkdir /tmp/test-results
41
- TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
42
- circleci tests split --split-by=timings)"
43
-
44
- bundle exec rspec \
45
- --format progress \
46
- --format RspecJunitFormatter \
47
- --out /tmp/test-results/rspec.xml \
48
- --format progress \
49
- $TEST_FILES
50
-
51
- # collect reports
52
- - store_artifacts:
53
- path: /tmp/rspec/
54
- destination: rspec
55
-
56
- - store_test_results:
57
- path: /tmp/rspec/
17
+ - ruby/install-deps:
18
+ key: gems-v2
19
+ include-branch-in-cache-key: false
20
+ - ruby/rspec-test
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7.4
1
+ 3.2.2
data/.travis.yml CHANGED
@@ -3,5 +3,5 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.6.1
6
+ - 3.2.2
7
7
  before_install: gem install bundler -v 1.17.2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # v2.0.0 2023-08-31
2
+ - Add simpler initialization interface for one-off clients
3
+ - Add helpers for common config options
4
+ - Require request timeouts
5
+
6
+ # v1.1.0 2023-04-03
7
+ - Introduce a Typhoeus::Hydra interface for batch executions
8
+
1
9
  # v1.0.4 2022-06-08
2
10
  - Fix bug where callbacks were shared between unrelated child client class ([#130](https://github.com/nxt-insurance/nxt_http_client/pull/130))
3
11
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nxt_http_client (1.0.4)
4
+ nxt_http_client (2.0.0)
5
5
  activesupport (< 8.0)
6
6
  nxt_registry
7
7
  typhoeus
@@ -9,60 +9,64 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- activesupport (7.0.3)
12
+ activesupport (7.0.7.2)
13
13
  concurrent-ruby (~> 1.0, >= 1.0.2)
14
14
  i18n (>= 1.6, < 2)
15
15
  minitest (>= 5.1)
16
16
  tzinfo (~> 2.0)
17
- addressable (2.8.0)
18
- public_suffix (>= 2.0.2, < 5.0)
17
+ addressable (2.8.4)
18
+ public_suffix (>= 2.0.2, < 6.0)
19
19
  coderay (1.1.3)
20
- concurrent-ruby (1.1.10)
20
+ concurrent-ruby (1.2.2)
21
+ connection_pool (2.4.1)
21
22
  crack (0.4.5)
22
23
  rexml
23
24
  diff-lcs (1.5.0)
24
- ethon (0.15.0)
25
+ ethon (0.16.0)
25
26
  ffi (>= 1.15.0)
26
27
  ffi (1.15.5)
27
28
  hashdiff (1.0.1)
28
- i18n (1.10.0)
29
+ i18n (1.14.1)
29
30
  concurrent-ruby (~> 1.0)
30
31
  method_source (1.0.0)
31
- minitest (5.15.0)
32
+ minitest (5.19.0)
32
33
  nxt_registry (0.3.10)
33
34
  activesupport
34
35
  nxt_vcr_harness (0.1.4)
35
36
  rspec (~> 3.0)
36
37
  vcr (~> 6.0)
37
- pry (0.14.1)
38
+ pry (0.14.2)
38
39
  coderay (~> 1.1)
39
40
  method_source (~> 1.0)
40
- public_suffix (4.0.7)
41
+ public_suffix (5.0.3)
41
42
  rake (13.0.6)
42
- redis (4.6.0)
43
- rexml (3.2.5)
44
- rspec (3.11.0)
45
- rspec-core (~> 3.11.0)
46
- rspec-expectations (~> 3.11.0)
47
- rspec-mocks (~> 3.11.0)
48
- rspec-core (3.11.0)
49
- rspec-support (~> 3.11.0)
50
- rspec-expectations (3.11.0)
43
+ redis (5.0.6)
44
+ redis-client (>= 0.9.0)
45
+ redis-client (0.14.1)
46
+ connection_pool
47
+ rexml (3.2.6)
48
+ rspec (3.12.0)
49
+ rspec-core (~> 3.12.0)
50
+ rspec-expectations (~> 3.12.0)
51
+ rspec-mocks (~> 3.12.0)
52
+ rspec-core (3.12.2)
53
+ rspec-support (~> 3.12.0)
54
+ rspec-expectations (3.12.3)
51
55
  diff-lcs (>= 1.2.0, < 2.0)
52
- rspec-support (~> 3.11.0)
53
- rspec-mocks (3.11.1)
56
+ rspec-support (~> 3.12.0)
57
+ rspec-mocks (3.12.6)
54
58
  diff-lcs (>= 1.2.0, < 2.0)
55
- rspec-support (~> 3.11.0)
56
- rspec-support (3.11.0)
57
- rspec_junit_formatter (0.5.1)
59
+ rspec-support (~> 3.12.0)
60
+ rspec-support (3.12.1)
61
+ rspec_junit_formatter (0.6.0)
58
62
  rspec-core (>= 2, < 4, != 2.12.0)
59
- timecop (0.9.5)
63
+ timecop (0.9.6)
60
64
  typhoeus (1.4.0)
61
65
  ethon (>= 0.9.0)
62
- tzinfo (2.0.4)
66
+ tzinfo (2.0.6)
63
67
  concurrent-ruby (~> 1.0)
64
- vcr (6.1.0)
65
- webmock (3.14.0)
68
+ vcr (6.2.0)
69
+ webmock (3.18.1)
66
70
  addressable (>= 2.8.0)
67
71
  crack (>= 0.3.2)
68
72
  hashdiff (>= 0.4.0, < 2.0.0)
data/README.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # NxtHttpClient
2
2
 
3
- Build http clients with ease. NxtHttpClient is a simple DSL on top of the [typhoeus](https://github.com/typhoeus/typhoeus)
4
- gem. NxtHttpClient mostly provides you a simple configuration functionality to setup http connections on the class level.
5
- Furthermore it's mostly a callback framework that allows you to seamlessly handle your responses. Since it's is just a simple
6
- layer on top of [typhoeus](https://github.com/typhoeus/typhoeus) it also allows to access and configure the original
3
+ Build http clients with ease. NxtHttpClient is a DSL on top of the [typhoeus](https://github.com/typhoeus/typhoeus)
4
+ gem. NxtHttpClient provides configuration functionality to set up HTTP connections on the class level, and attach
5
+ callbacks that allow you to seamlessly handle responses, as well as configure the original
7
6
  `Typhoeus::Request` before making a request.
8
7
 
9
8
 
@@ -17,102 +16,114 @@ gem 'nxt_http_client'
17
16
 
18
17
  And then execute:
19
18
 
20
- $ bundle
21
-
22
- Or install it yourself as:
23
-
24
- $ gem install nxt_http_client
19
+ ```sh
20
+ bundle
21
+ ````
25
22
 
26
23
  ## Usage
27
24
 
28
- A typical client could look something like this:
25
+ With NxtHttpClient, you can create client classes for interacting with external services:
29
26
 
30
27
  ```ruby
31
- class UserFetcher < Client
32
- def initialize(id)
33
- @url = ".../users/#{id}"
34
- end
35
-
36
- def call
37
- get(url) do |response_handler|
38
- response_handler.on(:success) do |response|
39
- JSON(response.body)
40
- end
41
- end
42
- end
43
-
44
- private
45
-
46
- attr_reader :url
47
- end
48
- ```
49
-
50
- In order to setup a shared configuration you would therefore setup a client base class. The configuration and any
51
- response handler or callbacks you setup in your base class are then inherited to your concrete client implementations.
52
-
53
- ```ruby
54
- class Client < NxtHttpClient
28
+ class UserServiceClient < NxtHttpClient::Client
29
+ # Set a base URL, and any other request options you need
55
30
  configure do |config|
56
31
  config.base_url = 'www.example.com'
57
32
  config.request_options.deep_merge!(
58
33
  headers: { API_KEY: '1993' },
59
- method: :get,
60
34
  followlocation: true
61
35
  )
36
+ config.json_request = true
37
+ config.raise_response_errors = true
62
38
  config.x_request_id_proc = -> { ('a'..'z').to_a.shuffle.take(10).join }
63
39
  end
64
40
 
41
+ # You may add a log handler if you wish...
65
42
  log do |info|
66
43
  Rails.logger.info(info)
67
44
  end
68
45
 
46
+ # ...as well as a response handler
69
47
  response_handler do |handler|
48
+ # Note: This error handler is set by default when you use
49
+ # config.raise_response_errors = true
70
50
  handler.on(:error) do |response|
71
- Raven.extra_context(error_details: error.to_h)
51
+ Sentry.set_extras(http_error_details: error.to_h)
72
52
  raise StandardError, "I can't handle this: #{response.code}"
73
53
  end
74
54
  end
75
55
  end
76
56
  ```
77
57
 
78
- ### HTTP Methods
79
-
80
- In order to build a request and execute it NxtHttpClient implements all http standard methods.
58
+ and then child classes for accessing specific endpoints and adding custom behaviours.
81
59
 
82
60
  ```ruby
83
- class Client < NxtHttpClient
84
- def initialize(url)
85
- @url = url
61
+ class UserFetcher < UserServiceClient
62
+ def initialize(id)
63
+ @url = ".../users/#{id}"
86
64
  end
87
65
 
88
- attr_reader :url
89
-
90
- def fetch
91
- get(url) do
92
- handler.on(:success) { |response| response.body }
66
+ def fetch_email
67
+ get(url, { fields: :email }) do |response_handler|
68
+ response_handler.on(:success) do |response|
69
+ JSON(response.body)['email']
70
+ end
93
71
  end
94
72
  end
95
73
 
96
- def create(params)
97
- post(url, params: params) do
98
- handler.on(:success) { |response| response.body }
74
+ def fetch_user_details
75
+ get(url) do |response_handler|
76
+ response_handler.on(:success) do |response|
77
+ body = JSON(response.body)
78
+ User.new(body)
79
+ end
99
80
  end
100
81
  end
101
82
 
102
- def update(params)
103
- put(url, params: params) do
104
- handler.on(:success) { |response| response.body }
105
- end
106
- end
83
+ private attr_reader :url
84
+ end
85
+ ```
86
+
87
+ Usage:
88
+
89
+ ```ruby
90
+ client = UserFetcher.new('1234')
91
+ client.fetch_email
92
+ client.fetch_user_details
93
+ ```
94
+
95
+ However, if you need a simple ad hoc client for a one-off task, you can use `.make` to instantiate one.
107
96
 
108
- # ... there are others as you know ...
97
+ ```ruby
98
+ client = NxtHttpClient::Client.make do
99
+ configure do |config|
100
+ config.base_url = 'www.httpstat.us'
101
+ config.request_options.deep_merge!(
102
+ headers: { API_KEY: '1993' },
103
+ followlocation: true
104
+ )
105
+ config.json_request = true
106
+ config.json_response = true
107
+ end
109
108
  end
109
+
110
+ client.get('/data')
111
+ client.post('/data', body: { some: 'content'})
110
112
  ```
111
113
 
112
114
  ### configure
113
115
 
114
- Register your default request options on the class level. Available options are `request_options` that are passed
115
- directly to the underlying Typhoeus Request. Then there is `base_url` and `x_request_id_proc`.
116
+ Register your default request options on the class level. Available options are:
117
+ - `request_options`, passed directly to the underlying Typhoeus Request
118
+ - `base_url=`
119
+ - `x_request_id_proc=`
120
+ - `json_request=`: Shorthand to set the Content-Type request header to JSON and automatically convert request bodies to JSON
121
+ - `json_response=`: Shorthand to set the Accept request header and automatically convert success response bodies to JSON
122
+ - `raise_response_errors=`: Makes the client raise a `NxtHttpClient::Error` for a non-success response.
123
+ You can also do this manually by setting a response_handler.
124
+ - `bearer_auth=`: Set a bearer token to be sent in the Authorization header
125
+ - `basic_auth=`: Pass a Hash containing `:username` and `:password`, to be sent as Basic credentials in the Authorization header
126
+ - `timeouts(total:, connect: nil)`: Configure timeouts
116
127
 
117
128
  ### response_handler
118
129
 
@@ -121,7 +132,7 @@ on the instance level.
121
132
 
122
133
  ### fire
123
134
 
124
- All http methods internally are delegate to `fire('uri', **request_options)`. Since `fire` is a public method you can
135
+ All http methods internally are delegate to `fire(uri, **request_options)`. Since `fire` is a public method you can
125
136
  also use it to fire your requests and use the response handler to register callbacks for specific responses.
126
137
 
127
138
  Registered callbacks have a hierarchy by which they are executed. Specific callbacks will come first
@@ -194,7 +205,22 @@ requires the response for initialization. Furthermore it has a handy `to_h` meth
194
205
  the request and response.
195
206
 
196
207
  #### Timeouts
197
- NxtHttpClient::Error exposes the `timed_out?` method from `Typhoeus::Response`, so you can check if an error is raised due to a timeout. This is useful when setting a custom timeout value in your configuration.
208
+ **You must set a timeout on every request (or when configuring the client class).**
209
+ Otherwise, this gem will raise an error. The idea is to enforce the best practice
210
+ of [always setting a timeout](https://dev.to/bearer/the-importance-of-request-timeouts-l3n).
211
+
212
+ To set a timeout, use the `timeout_seconds` config method:
213
+
214
+ ```rb
215
+ configure do |config|
216
+ config.timeout_seconds(total: 10)
217
+ # You can also set a connect timeout
218
+ config.timeout_seconds(total: 10, connecttimeout: 2)
219
+ end
220
+ ```
221
+
222
+ NxtHttpClient::Error exposes the `timed_out?` method from `Typhoeus::Response`, so you can check if an error is raised due to a timeout.
223
+ This is useful when setting a custom timeout value in your configuration.
198
224
 
199
225
  ### Logging
200
226
 
@@ -248,7 +274,25 @@ end
248
274
 
249
275
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
250
276
 
251
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
277
+ To install this gem onto your local machine, run `bundle exec rake install`.
278
+
279
+ ## Releasing
280
+
281
+ First, if you don't want to always log in with your RubyGems password,
282
+ you can create an API key on Rubygems.org, and then run:
283
+
284
+ ```shell
285
+ bundle config set --local gem.push_key rubygems
286
+ ```
287
+
288
+ Add to `~/.gem/credentials` (create if it doesn't exist):
289
+
290
+ ```shell
291
+ :rubygems: <your Rubygems API key>
292
+ ```
293
+
294
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`,
295
+ which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
252
296
 
253
297
  ## Contributing
254
298
 
@@ -0,0 +1,26 @@
1
+ module NxtHttpClient
2
+ def self.execute_in_batch(*client_instances, ignore_around_callbacks: false, raise_errors: true)
3
+ client_map = Hash.new do |hash, key|
4
+ hash[key] = { request: nil, error: nil, result: nil }
5
+ end
6
+
7
+ client_instances.each do |client|
8
+ client.singleton_class.include(NxtHttpClient::Client::BatchPatch)
9
+ client.assign_batch_data(client_map[client], ignore_around_callbacks)
10
+ end
11
+
12
+ hydra = Typhoeus::Hydra.new
13
+
14
+ client_instances.each do |client|
15
+ client.call.tap do |request|
16
+ hydra.queue(request)
17
+ end
18
+ end
19
+
20
+ hydra.run
21
+
22
+ client_map.map do |client, response_data|
23
+ client.finish(response_data[:request], response_data[:result], response_data[:error], raise_errors: raise_errors)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,55 @@
1
+ module NxtHttpClient
2
+ class Client
3
+ module BatchPatch
4
+ attr_reader :callback_map, :ignore_around_callbacks
5
+
6
+ def assign_batch_data(callback_map, ignore_around_callbacks)
7
+ @callback_map = callback_map
8
+ @ignore_around_callbacks = ignore_around_callbacks
9
+ end
10
+
11
+ def fire(url = '', **opts, &block)
12
+ response_handler = build_response_handler(opts[:response_handler], &block)
13
+ request = build_request(url, **opts.except(:response_handler))
14
+ callback_map[:request] = request
15
+
16
+ setup_on_headers_callback(request, response_handler)
17
+ setup_on_body_callback(request, response_handler)
18
+
19
+ request.on_complete do |response|
20
+ callback_map[:result] = callback_or_response(response, response_handler)
21
+ rescue StandardError => e
22
+ callback_map[:error] = e
23
+ end
24
+
25
+ if callbacks.any_around_callbacks? && ignore_around_callbacks != true
26
+ raise(
27
+ ArgumentError,
28
+ <<~TXT
29
+ `around_fire` callbacks are not supported when firing batches. \
30
+ Pass `ignore_around_callbacks: true` to `execute_in_batch` \
31
+ in order to acknowledge and muffle this.
32
+ TXT
33
+ )
34
+ end
35
+
36
+ run_before_fire_callbacks(request, response_handler)
37
+
38
+ request
39
+ end
40
+
41
+ def finish(request, result, error, raise_errors: true)
42
+ result = run_after_fire_callbacks(request, request.response, result, error)
43
+
44
+ case [error, raise_errors]
45
+ in [nil, _]
46
+ result
47
+ in [_, true]
48
+ raise error
49
+ in [_, false]
50
+ error
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,13 +1,34 @@
1
1
  module NxtHttpClient
2
+ # The entry point to this gem. The Client class is designed to be extended into custom base classes,
3
+ # but you can also create a one-off instance with the `.make` method.`
2
4
  class Client
3
5
  extend ClientDsl
6
+
4
7
  CACHE_STRATEGIES = %w[global thread].freeze
5
8
  HTTP_METHODS = %w[get post patch put delete head].freeze
6
9
 
10
+ # Get an anonymous client for one-off use. Example:
11
+ #
12
+ # client = NxtHttpClient::Client.make do
13
+ # configure do |config|
14
+ # config.base_url = 'www.httpstat.us'
15
+ # end
16
+ # end
17
+ # client.get('200')
18
+ def self.make(&block)
19
+ Class.new(self, &block).new
20
+ end
21
+
7
22
  def build_request(url, **opts)
8
23
  url = build_url(opts, url)
9
24
  opts = build_headers(opts)
10
25
 
26
+ set_timeouts(opts)
27
+
28
+ if config.json_request
29
+ opts[:body] = opts[:body].to_json # Typhoeus requires userland JSON encoding
30
+ end
31
+
11
32
  Typhoeus::Request.new(url, **opts.symbolize_keys)
12
33
  end
13
34
 
@@ -41,7 +62,7 @@ module NxtHttpClient
41
62
 
42
63
  HTTP_METHODS.each do |method|
43
64
  define_method method do |url = '', **opts, &block|
44
- fire(url, **opts.reverse_merge(method: method), &block)
65
+ fire(url, **opts.reverse_merge(method:), &block)
45
66
  end
46
67
  end
47
68
 
@@ -59,10 +80,22 @@ module NxtHttpClient
59
80
  opts = config.request_options.with_indifferent_access.deep_merge(opts.with_indifferent_access)
60
81
  opts[:headers] ||= {}
61
82
 
62
- if config.x_request_id_proc
63
- opts[:headers][XRequestId] ||= config.x_request_id_proc.call
83
+ opts[:headers]['Content-Type'] ||= ApplicationJson if config.json_request
84
+ opts[:headers]['Accept'] ||= ApplicationJson if config.json_response
85
+
86
+ if config.basic_auth
87
+ begin
88
+ config.basic_auth => { username:, password: }
89
+ rescue NoMatchingPatternKeyError
90
+ raise ArgumentError, 'basic_auth must be a Hash with :username and :password'
91
+ end
92
+ opts[:userpwd] ||= "#{username}:#{password}"
93
+ elsif (bearer_token = config.bearer_auth)
94
+ opts[:headers]['Authorization'] ||= "Bearer #{bearer_token}"
64
95
  end
65
96
 
97
+ opts[:headers][XRequestId] ||= config.x_request_id_proc.call if config.x_request_id_proc
98
+
66
99
  build_cache_header(opts)
67
100
  opts
68
101
  end
@@ -91,6 +124,15 @@ module NxtHttpClient
91
124
  end
92
125
  end
93
126
 
127
+ def set_timeouts(opts)
128
+ if (timeouts = config.timeouts).is_a?(Hash)
129
+ opts[:timeout] ||= timeouts[:total]
130
+ opts[:connecttimeout] ||= timeouts[:connect]
131
+ end
132
+
133
+ raise ArgumentError, 'You must configure a total timeout for this client or request' unless timeout_configured?(opts)
134
+ end
135
+
94
136
  def url_without_duplicated_hashes(url)
95
137
  duplicated_slashes = url.match(/([^:]\/{2,})/)
96
138
  duplicated_slashes && duplicated_slashes.captures.each do |capture|
@@ -107,6 +149,26 @@ module NxtHttpClient
107
149
 
108
150
  def build_response_handler(handler, &block)
109
151
  response_handler = handler || dup_handler_from_class || NxtHttpClient::ResponseHandler.new
152
+
153
+ if config.json_response
154
+ response_handler.configure do |handler|
155
+ handler.on(:success) do |response|
156
+ response.define_singleton_method(:body) { JSON(response.response_body) }
157
+ response
158
+ end
159
+ end
160
+ end
161
+
162
+ if config.raise_response_errors
163
+ response_handler.configure do |handler|
164
+ handler.on(:error) do |response|
165
+ error = NxtHttpClient::Error.new(response)
166
+ ::Sentry.set_extras(http_error_details: error.to_h) if defined?(::Sentry)
167
+ raise error
168
+ end
169
+ end
170
+ end
171
+
110
172
  response_handler.configure(&block) if block_given?
111
173
  response_handler
112
174
  end
@@ -155,5 +217,9 @@ module NxtHttpClient
155
217
  def callbacks
156
218
  @callbacks ||= self.class.callbacks
157
219
  end
220
+
221
+ def timeout_configured?(opts)
222
+ [:timeout, :timeout_ms].any? { opts[_1].present? }
223
+ end
158
224
  end
159
225
  end
@@ -52,8 +52,6 @@ module NxtHttpClient
52
52
  @response_handler
53
53
  end
54
54
 
55
- alias_method :response_handler, :response_handler
56
-
57
55
  private
58
56
 
59
57
  def client_ancestors
@@ -1,15 +1,40 @@
1
1
  module NxtHttpClient
2
- CONFIGURABLE_OPTIONS = %i[request_options base_url x_request_id_proc].freeze
2
+ CONFIGURABLE_OPTIONS = {
3
+ request_options: ActiveSupport::HashWithIndifferentAccess.new,
4
+ base_url: '',
5
+ x_request_id_proc: nil,
3
6
 
4
- Config = Struct.new('Config', *CONFIGURABLE_OPTIONS) do
5
- def initialize(request_options: ActiveSupport::HashWithIndifferentAccess.new, base_url: '', x_request_id_proc: nil)
6
- self.request_options = request_options
7
- self.base_url = base_url
8
- self.x_request_id_proc = x_request_id_proc
7
+ # Helper to set the Content-Type request header and automatically convert request bodies to JSON
8
+ json_request: false,
9
+ # Helper to set the Accept request header and automatically convert success response bodies to JSON
10
+ json_response: false,
11
+ raise_response_errors: false,
12
+
13
+ bearer_auth: nil,
14
+ basic_auth: nil,
15
+ timeouts: nil,
16
+ }.freeze
17
+
18
+ Config = Struct.new('Config', *CONFIGURABLE_OPTIONS.keys) do
19
+ def initialize
20
+ CONFIGURABLE_OPTIONS.each do |key, default_value|
21
+ self.send(:"#{key}=", default_value.dup)
22
+ end
23
+ end
24
+
25
+ def timeout_seconds(total:, connect: nil)
26
+ timeouts = { total:, connect:, }.compact
27
+
28
+ self.timeouts = timeouts
9
29
  end
10
30
 
11
31
  def dup
12
- self.class.new(**to_h.deep_dup)
32
+ options = to_h
33
+ self.class.new.tap do |instance|
34
+ options.each do |key, value|
35
+ instance.send(:"#{key}=", value.dup)
36
+ end
37
+ end
13
38
  end
14
39
  end
15
40
  end
@@ -30,6 +30,10 @@ module NxtHttpClient
30
30
  end
31
31
  end
32
32
 
33
+ def any_around_callbacks?
34
+ registry.resolve(:around).any?
35
+ end
36
+
33
37
  def any_after_callbacks?
34
38
  registry.resolve(:after).any?
35
39
  end
@@ -1,3 +1,3 @@
1
1
  module NxtHttpClient
2
- VERSION = "1.0.4"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -11,6 +11,8 @@ require 'nxt_http_client/logger'
11
11
  require 'nxt_http_client/fire_callbacks'
12
12
  require 'nxt_http_client/client_dsl'
13
13
  require 'nxt_http_client/client'
14
+ require 'nxt_http_client/client/batch_patch'
15
+ require 'nxt_http_client/batch_execution'
14
16
  require 'nxt_http_client/error'
15
17
 
16
18
  module NxtHttpClient
@@ -8,23 +8,11 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Andreas Robecke", "Nils Sommer", "Raphael Kallensee", "Luetfi Demirci"]
9
9
  spec.email = ["a.robecke@getsafe.de"]
10
10
 
11
- spec.summary = %q{NxtHttpClinet is a simple DSL on top the typhoeus http gem}
12
- spec.description = %q{NxtHttpClinet allows you to easily create and configure http clients.}
13
- spec.homepage = "https://github.com/nxt-insurance"
11
+ spec.summary = %q{NxtHttpClient is a simple DSL on top the typhoeus http gem}
12
+ spec.description = %q{NxtHttpClient allows you to easily create and configure http clients.}
13
+ spec.homepage = "https://github.com/nxt-insurance/nxt_http_client"
14
14
  spec.license = "MIT"
15
15
 
16
- # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
- # to allow pushing to a single host or delete this section to allow pushing to any host.
18
- if spec.respond_to?(:metadata)
19
- spec.metadata["allowed_push_host"] = 'https://rubygems.org'
20
-
21
- spec.metadata["homepage_uri"] = spec.homepage
22
- spec.metadata["source_code_uri"] = "https://github.com/nxt-insurance/nxt_http_client"
23
- else
24
- raise "RubyGems 2.0 or newer is required to protect against " \
25
- "public gem pushes."
26
- end
27
-
28
16
  # Specify which files should be added to the gem when it is released.
29
17
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
30
18
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nxt_http_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Robecke
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2022-06-08 00:00:00.000000000 Z
14
+ date: 2023-08-31 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: typhoeus
@@ -195,7 +195,7 @@ dependencies:
195
195
  - - ">="
196
196
  - !ruby/object:Gem::Version
197
197
  version: '0'
198
- description: NxtHttpClinet allows you to easily create and configure http clients.
198
+ description: NxtHttpClient allows you to easily create and configure http clients.
199
199
  email:
200
200
  - a.robecke@getsafe.de
201
201
  executables: []
@@ -216,7 +216,9 @@ files:
216
216
  - bin/console
217
217
  - bin/setup
218
218
  - lib/nxt_http_client.rb
219
+ - lib/nxt_http_client/batch_execution.rb
219
220
  - lib/nxt_http_client/client.rb
221
+ - lib/nxt_http_client/client/batch_patch.rb
220
222
  - lib/nxt_http_client/client_dsl.rb
221
223
  - lib/nxt_http_client/config.rb
222
224
  - lib/nxt_http_client/error.rb
@@ -227,13 +229,10 @@ files:
227
229
  - lib/nxt_http_client/undefined.rb
228
230
  - lib/nxt_http_client/version.rb
229
231
  - nxt_http_client.gemspec
230
- homepage: https://github.com/nxt-insurance
232
+ homepage: https://github.com/nxt-insurance/nxt_http_client
231
233
  licenses:
232
234
  - MIT
233
- metadata:
234
- allowed_push_host: https://rubygems.org
235
- homepage_uri: https://github.com/nxt-insurance
236
- source_code_uri: https://github.com/nxt-insurance/nxt_http_client
235
+ metadata: {}
237
236
  post_install_message:
238
237
  rdoc_options: []
239
238
  require_paths:
@@ -249,8 +248,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
249
248
  - !ruby/object:Gem::Version
250
249
  version: '0'
251
250
  requirements: []
252
- rubygems_version: 3.1.6
251
+ rubygems_version: 3.4.10
253
252
  signing_key:
254
253
  specification_version: 4
255
- summary: NxtHttpClinet is a simple DSL on top the typhoeus http gem
254
+ summary: NxtHttpClient is a simple DSL on top the typhoeus http gem
256
255
  test_files: []