berkeley_library-util 0.1.6 → 0.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a17e4e3e2e6e03e0dda795450e724650a0a7f7ae44db3b7a558bc371d8bed1b7
4
- data.tar.gz: 106ff7e21c1e9568f4bdabc116023c292878f4fd629e5ee3ada934b2c2a10b43
3
+ metadata.gz: 71143c349265481e222ad5c0330ad1f995697139d114f9b9a2615c8d9cb8ea4b
4
+ data.tar.gz: 3ad66018de9e3ff7efe0d5637c9c79ea616da7510aff3c6f8f9bc07ed83cbb59
5
5
  SHA512:
6
- metadata.gz: b4fbde8aa9e43a7ad7c414b92b488c5d2af1ae29c7f28a7ecd8f6ceb308e42c3c27a2256994362b15cba5133ec00feb32d5fe76f0b774577b292be6c2ba08528
7
- data.tar.gz: 23c41cbed1b0befc458c8989792c45cf7429905b06e2deec72a88478f8eb7676a6e3be96dae5fa308239bb4f70b19acae421593711c20dd22d305f55262b0f64
6
+ metadata.gz: f0563b8e84cdeff86055d4423ddf57a6559c7e9a80527b643fe9cf4c10db2a62cbd8e122fc0045da56778b00ac6486ebff8ab00a5452721f385a93e9f8fc6e12
7
+ data.tar.gz: c19e2f817542e98ec80b9e05faf6bdaad821ab8f6589638c3790611de8231cd7bbf2a68f3d0a520fb7b96a8b0c16ac06685ae547ad6c5b7023468bea50ec4f01
data/CHANGES.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 0.1.7 (2023-03-15)
2
+
3
+ - Allow passing `log: false` to `Requester` methods (and corresponding `URIs` convenience
4
+ methods) to suppress logging of each request URL and response code.
5
+ - Allow constructing `Requester` instances with delayed execution
6
+ - Fix documentation for `get_response` and `head_response` in `URIs` and `Requester`
7
+
1
8
  # 0.1.6 (2023-03-09)
2
9
 
3
10
  - Fix issue in `Requester` where query parameters would not be appended properly
@@ -7,7 +7,7 @@ module BerkeleyLibrary
7
7
  SUMMARY = 'Miscellaneous Ruby utilities for the UC Berkeley Library'.freeze
8
8
  DESCRIPTION = 'A collection of miscellaneous Ruby routines for the UC Berkeley Library.'.freeze
9
9
  LICENSE = 'MIT'.freeze
10
- VERSION = '0.1.6'.freeze
10
+ VERSION = '0.1.7'.freeze
11
11
  HOMEPAGE = 'https://github.com/BerkeleyLibrary/util'.freeze
12
12
  end
13
13
  end
@@ -39,6 +39,14 @@ module BerkeleyLibrary
39
39
  end
40
40
  end
41
41
 
42
+ # Returns the new URI as a string.
43
+ #
44
+ # @return [String] a new URI appending the joined path elements, as a string.
45
+ # @raise URI::InvalidComponentError if appending the specified elements would create an invalid URI
46
+ def to_url_str
47
+ to_uri.to_s
48
+ end
49
+
42
50
  private
43
51
 
44
52
  def handle_element(element, elem_index)
@@ -6,19 +6,23 @@ require 'berkeley_library/logging'
6
6
  module BerkeleyLibrary
7
7
  module Util
8
8
  module URIs
9
- module Requester
10
- class << self
11
- include BerkeleyLibrary::Logging
9
+ class Requester
10
+ include BerkeleyLibrary::Logging
11
+
12
+ # ------------------------------------------------------------
13
+ # Class methods
12
14
 
15
+ class << self
13
16
  # Performs a GET request and returns the response body as a string.
14
17
  #
15
18
  # @param uri [URI, String] the URI to GET
16
19
  # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
17
20
  # @param headers [Hash] the request headers.
18
21
  # @return [String] the body as a string.
22
+ # @param log [Boolean] whether to log each request URL and response code
19
23
  # @raise [RestClient::Exception] in the event of an unsuccessful request.
20
- def get(uri, params: {}, headers: {})
21
- resp = make_request(:get, uri, params, headers)
24
+ def get(uri, params: {}, headers: {}, log: true)
25
+ resp = make_request(:get, uri, params, headers, log)
22
26
  resp.body
23
27
  end
24
28
 
@@ -29,9 +33,10 @@ module BerkeleyLibrary
29
33
  # @param uri [URI, String] the URI to HEAD
30
34
  # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
31
35
  # @param headers [Hash] the request headers.
36
+ # @param log [Boolean] whether to log each request URL and response code
32
37
  # @return [Integer] the response code as an integer.
33
- def head(uri, params: {}, headers: {})
34
- head_response(uri, params: params, headers: headers).code
38
+ def head(uri, params: {}, headers: {}, log: true)
39
+ head_response(uri, params: params, headers: headers, log: log).code
35
40
  end
36
41
 
37
42
  # Performs a GET request and returns the response, even in the event of
@@ -40,9 +45,10 @@ module BerkeleyLibrary
40
45
  # @param uri [URI, String] the URI to GET
41
46
  # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
42
47
  # @param headers [Hash] the request headers.
48
+ # @param log [Boolean] whether to log each request URL and response code
43
49
  # @return [RestClient::Response] the body as a string.
44
- def get_response(uri, params: {}, headers: {})
45
- make_request(:get, uri, params, headers)
50
+ def get_response(uri, params: {}, headers: {}, log: true)
51
+ make_request(:get, uri, params, headers, log)
46
52
  rescue RestClient::Exception => e
47
53
  e.response
48
54
  end
@@ -53,56 +59,105 @@ module BerkeleyLibrary
53
59
  # @param uri [URI, String] the URI to HEAD
54
60
  # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
55
61
  # @param headers [Hash] the request headers.
56
- # @return [RestClient::Response] the body as a string.
57
- def head_response(uri, params: {}, headers: {})
58
- make_request(:head, uri, params, headers)
62
+ # @param log [Boolean] whether to log each request URL and response code
63
+ # @return [RestClient::Response] the response
64
+ def head_response(uri, params: {}, headers: {}, log: true)
65
+ make_request(:head, uri, params, headers, log)
59
66
  rescue RestClient::Exception => e
60
67
  e.response
61
68
  end
62
69
 
63
70
  private
64
71
 
65
- # @return [RestClient::Response]
66
- def make_request(method, uri, params, headers)
67
- url_str = url_str_with_params(uri, params)
68
- req_resp_or_raise(method, url_str, headers)
72
+ def make_request(method, url, params, headers, log)
73
+ Requester.new(method, url, params: params, headers: headers, log: log).make_request
69
74
  end
75
+ end
76
+
77
+ # ------------------------------------------------------------
78
+ # Constants
79
+
80
+ SUPPORTED_METHODS = %i[get head].freeze
70
81
 
71
- def url_str_with_params(url, params)
72
- raise ArgumentError, 'url cannot be nil' unless (uri = Validator.uri_or_nil(url))
82
+ # ------------------------------------------------------------
83
+ # Attributes
73
84
 
74
- elements = [].tap do |ee|
75
- ee << uri
76
- next if params.empty?
85
+ attr_reader :method, :url_str, :headers, :log
77
86
 
78
- ee << (uri.query ? '&' : '?')
79
- ee << URI.encode_www_form(params)
80
- end
87
+ # ------------------------------------------------------------
88
+ # Initializer
81
89
 
82
- uri = Appender.new(*elements).to_uri
83
- uri.to_s
90
+ # Initializes a new Requester.
91
+ #
92
+ # @param method [:get, :head] the HTTP method to use
93
+ # @param url [String, URI] the URL or URI to request
94
+ # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
95
+ # @param headers [Hash] the request headers.
96
+ # @param log [Boolean] whether to log each request URL and response code
97
+ # @raise URI::InvalidURIError if the specified URL is invalid
98
+ def initialize(method, url, params: {}, headers: {}, log: true)
99
+ raise ArgumentError, "#{method} not supported" unless SUPPORTED_METHODS.include?(method)
100
+ raise ArgumentError, 'url cannot be nil' unless (uri = Validator.uri_or_nil(url))
101
+
102
+ @method = method
103
+ @url_str = url_str_with_params(uri, params)
104
+ @headers = headers
105
+ @log = log
106
+ end
107
+
108
+ # ------------------------------------------------------------
109
+ # Public instance methods
110
+
111
+ # @return [RestClient::Response]
112
+ def make_request
113
+ execute_request.tap do |resp|
114
+ log_response(resp)
84
115
  end
116
+ rescue RestClient::Exception => e
117
+ log_response(e.response)
118
+ raise
119
+ end
120
+
121
+ # ------------------------------------------------------------
122
+ # Private methods
85
123
 
86
- # @return [RestClient::Response]
87
- def req_resp_or_raise(method, url_str, headers)
88
- resp = RestClient::Request.execute(method: method, url: url_str, headers: headers)
89
- begin
90
- return resp if (status = resp.code) == 200
91
-
92
- raise(exception_for(resp, status))
93
- ensure
94
- # noinspection RubyMismatchedReturnType
95
- logger.info("#{method.to_s.upcase} #{url_str} returned #{status}")
96
- end
124
+ private
125
+
126
+ def log_response(response)
127
+ return unless log
128
+
129
+ logger.info("#{method.to_s.upcase} #{url_str} returned #{response.code}")
130
+ end
131
+
132
+ def url_str_with_params(uri, params)
133
+ elements = [uri]
134
+ if params.any?
135
+ elements << (uri.query ? '&' : '?')
136
+ elements << URI.encode_www_form(params)
137
+ end
138
+
139
+ Appender.new(*elements).to_url_str
140
+ end
141
+
142
+ def execute_request
143
+ RestClient::Request.execute(method: method, url: url_str, headers: headers).tap do |response|
144
+ # Not all failed RestClient requests throw exceptions
145
+ raise(exception_for(response)) unless response.code == 200
97
146
  end
147
+ end
98
148
 
99
- def exception_for(resp, status)
100
- RestClient::RequestFailed.new(resp, status).tap do |ex|
101
- status_message = RestClient::STATUSES[status] || '(Unknown)'
102
- ex.message = "#{status} #{status_message}"
103
- end
149
+ def exception_for(resp)
150
+ status = resp.code
151
+ ex_class_for(status).new(resp, status).tap do |ex|
152
+ status_message = RestClient::STATUSES[status] || '(Unknown)'
153
+ ex.message = "#{status} #{status_message}"
104
154
  end
105
155
  end
156
+
157
+ def ex_class_for(status)
158
+ RestClient::Exceptions::EXCEPTIONS_MAP[status] || RestClient::RequestFailed
159
+ end
160
+
106
161
  end
107
162
  end
108
163
  end
@@ -31,10 +31,11 @@ module BerkeleyLibrary
31
31
  # @param uri [URI, String] the URI to GET
32
32
  # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
33
33
  # @param headers [Hash] the request headers.
34
+ # @param log [Boolean] whether to log each request URL and response code
34
35
  # @return [String] the body as a string.
35
36
  # @raise [RestClient::Exception] in the event of an unsuccessful request.
36
- def get(uri, params: {}, headers: {})
37
- Requester.get(uri, params: params, headers: headers)
37
+ def get(uri, params: {}, headers: {}, log: true)
38
+ Requester.get(uri, params: params, headers: headers, log: log)
38
39
  end
39
40
 
40
41
  # Performs a HEAD request and returns the response status as an integer.
@@ -44,9 +45,10 @@ module BerkeleyLibrary
44
45
  # @param uri [URI, String] the URI to HEAD
45
46
  # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
46
47
  # @param headers [Hash] the request headers.
48
+ # @param log [Boolean] whether to log each request URL and response code
47
49
  # @return [Integer] the response code as an integer.
48
- def head(uri, params: {}, headers: {})
49
- Requester.head(uri, params: params, headers: headers)
50
+ def head(uri, params: {}, headers: {}, log: true)
51
+ Requester.head(uri, params: params, headers: headers, log: log)
50
52
  end
51
53
 
52
54
  # Performs a GET request and returns the response, even in the event of
@@ -55,9 +57,10 @@ module BerkeleyLibrary
55
57
  # @param uri [URI, String] the URI to GET
56
58
  # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
57
59
  # @param headers [Hash] the request headers.
58
- # @return [RestClient::Response] the body as a string.
59
- def get_response(uri, params: {}, headers: {})
60
- Requester.get_response(uri, params: params, headers: headers)
60
+ # @param log [Boolean] whether to log each request URL and response code
61
+ # @return [RestClient::Response] the response
62
+ def get_response(uri, params: {}, headers: {}, log: true)
63
+ Requester.get_response(uri, params: params, headers: headers, log: log)
61
64
  end
62
65
 
63
66
  # Performs a HEAD request and returns the response, even in the event of
@@ -66,9 +69,10 @@ module BerkeleyLibrary
66
69
  # @param uri [URI, String] the URI to HEAD
67
70
  # @param params [Hash] the query parameters to add to the URI. (Note that the URI may already include query parameters.)
68
71
  # @param headers [Hash] the request headers.
69
- # @return [RestClient::Response] the body as a string.
70
- def head_response(uri, params: {}, headers: {})
71
- Requester.head_response(uri, params: params, headers: headers)
72
+ # @param log [Boolean] whether to log each request URL and response code
73
+ # @return [RestClient::Response] the response
74
+ def head_response(uri, params: {}, headers: {}, log: true)
75
+ Requester.head_response(uri, params: params, headers: headers, log: log)
72
76
  end
73
77
 
74
78
  # Returns the specified URL as a URI, or `nil` if the URL is `nil`.
@@ -145,6 +145,112 @@ module BerkeleyLibrary
145
145
  expect { Requester.head(nil) }.to raise_error(ArgumentError)
146
146
  end
147
147
  end
148
+
149
+ describe 'logging' do
150
+ attr_reader :logger
151
+
152
+ before do
153
+ @logger = instance_double(BerkeleyLibrary::Logging::Logger)
154
+ allow(BerkeleyLibrary::Logging).to receive(:logger).and_return(logger)
155
+ end
156
+
157
+ context 'GET' do
158
+ it 'logs request URLs and response codes for successful GET requests' do
159
+ url = 'https://example.org/'
160
+ expected_body = 'Help! I am trapped in a unit test'
161
+ stub_request(:get, url).to_return(body: expected_body)
162
+
163
+ expect(logger).to receive(:info).with(/#{url}.*200/)
164
+ Requester.send(:get, url)
165
+ end
166
+
167
+ it 'can suppress logging for successful GET requests' do
168
+ url = 'https://example.org/'
169
+ expected_body = 'Help! I am trapped in a unit test'
170
+ stub_request(:get, url).to_return(body: expected_body)
171
+
172
+ expect(logger).not_to receive(:info)
173
+ Requester.send(:get, url, log: false)
174
+ end
175
+
176
+ it 'logs request URLs and response codes for failed GET requests' do
177
+ url = 'https://example.org/'
178
+ status = 500
179
+ stub_request(:get, url).to_return(status: status)
180
+
181
+ expect(logger).to receive(:info).with(/#{url}.*#{status}/)
182
+ expect { Requester.send(:get, url) }.to raise_error(RestClient::InternalServerError)
183
+ end
184
+
185
+ it 'can suppress logging for failed GET requests' do
186
+ url = 'https://example.org/'
187
+ stub_request(:get, url).to_return(status: 500)
188
+
189
+ expect(logger).not_to receive(:info)
190
+ expect { Requester.send(:get, url, log: false) }.to raise_error(RestClient::InternalServerError)
191
+ end
192
+ end
193
+
194
+ context 'HEAD' do
195
+ it 'logs request URLs and response codes for successful HEAD requests' do
196
+ url = 'https://example.org/'
197
+ expected_body = 'Help! I am trapped in a unit test'
198
+ stub_request(:head, url).to_return(body: expected_body)
199
+
200
+ expect(logger).to receive(:info).with(/#{url}.*200/)
201
+ Requester.send(:head, url)
202
+ end
203
+
204
+ it 'can suppress logging for successful HEAD requests' do
205
+ url = 'https://example.org/'
206
+ expected_body = 'Help! I am trapped in a unit test'
207
+ stub_request(:head, url).to_return(body: expected_body)
208
+
209
+ expect(logger).not_to receive(:info)
210
+ Requester.send(:head, url, log: false)
211
+ end
212
+
213
+ it 'logs request URLs and response codes for failed HEAD requests' do
214
+ url = 'https://example.org/'
215
+ status = 500
216
+ stub_request(:head, url).to_return(status: status)
217
+
218
+ expect(logger).to receive(:info).with(/#{url}.*#{status}/)
219
+ expect(Requester.send(:head, url)).to eq(status)
220
+ end
221
+
222
+ it 'can suppress logging for failed HEAD requests' do
223
+ url = 'https://example.org/'
224
+ status = 500
225
+ stub_request(:head, url).to_return(status: status)
226
+
227
+ expect(logger).not_to receive(:info)
228
+ expect(Requester.send(:head, url, log: false)).to eq(status)
229
+ end
230
+ end
231
+ end
232
+
233
+ describe :new do
234
+ it 'rejects invalid URIs' do
235
+ url = 'not a uri'
236
+ Requester::SUPPORTED_METHODS.each do |method|
237
+ expect { Requester.new(method, url) }.to raise_error(URI::InvalidURIError)
238
+ end
239
+ end
240
+
241
+ it 'rejects nil URIs' do
242
+ Requester::SUPPORTED_METHODS.each do |method|
243
+ expect { Requester.new(method, nil) }.to raise_error(ArgumentError)
244
+ end
245
+ end
246
+
247
+ it 'rejects unsupported methods' do
248
+ url = 'https://example.org/'
249
+ %i[put patch post].each do |method|
250
+ expect { Requester.new(method, url) }.to raise_error(ArgumentError)
251
+ end
252
+ end
253
+ end
148
254
  end
149
255
  end
150
256
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: berkeley_library-util
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Moles
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-09 00:00:00.000000000 Z
11
+ date: 2023-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: berkeley_library-logging