berkeley_library-util 0.1.6 → 0.1.7

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: 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