Skip to content

Commit 6c04a55

Browse files
author
Ron Dahlgren
committed
Enhance SerpApiError, update usage
1 parent e0c020e commit 6c04a55

3 files changed

Lines changed: 102 additions & 12 deletions

File tree

lib/serpapi/client.rb

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,12 +234,31 @@ def get(endpoint, decoder = :json, params = {})
234234
# parse JSON response with Ruby standard library
235235
data = JSON.parse(response.body, symbolize_names: symbolize_names)
236236
if data.instance_of?(Hash) && data.key?(:error)
237-
raise SerpApiError, "HTTP request failed with error: #{data[:error]} from url: https://#{BACKEND}#{endpoint}, params: #{params}, decoder: #{decoder}, response status: #{response.status} "
237+
raise SerpApiError.new("HTTP request failed with error: #{data[:error]} from url: " +
238+
"https://#{BACKEND}#{endpoint}, params: #{params}, decoder: " +
239+
"#{decoder}, response status: #{response.status}",
240+
serpapi_error: data[:error],
241+
search_params: params,
242+
response_status: response.status,
243+
search_id: data.dig(:search_metadata, :id),
244+
decoder: decoder)
238245
elsif response.status != 200
239-
raise SerpApiError, "HTTP request failed with response status: #{response.status} reponse: #{data} on get url: https://#{BACKEND}#{endpoint}, params: #{params}, decoder: #{decoder}"
246+
raise SerpApiError.new("HTTP request failed with response status: #{response.status} " +
247+
" reponse: #{data} on get url: https://#{BACKEND}#{endpoint}, " +
248+
"params: #{params}, decoder: #{decoder}",
249+
serpapi_error: data[:error],
250+
search_params: params,
251+
response_status: response.status,
252+
search_id: data.dig(:search_metadata, :id),
253+
decoder: decoder)
240254
end
241255
rescue JSON::ParserError
242-
raise SerpApiError, "JSON parse error: #{response.body} on get url: https://#{BACKEND}#{endpoint}, params: #{params}, decoder: #{decoder}, response status: #{response.status}"
256+
raise SerpApiError.new("JSON parse error: #{response.body} on get url: " +
257+
"https://#{BACKEND}#{endpoint}, params: #{params}, " +
258+
"decoder: #{decoder}, response status: #{response.status}",
259+
search_params: params,
260+
response_status: response.status,
261+
decoder: decoder)
243262
end
244263

245264
# discard response body
@@ -249,7 +268,12 @@ def get(endpoint, decoder = :json, params = {})
249268
when :html
250269
# html decoder
251270
if response.status != 200
252-
raise SerpApiError, "HTTP request failed with response status: #{response.status} reponse: #{data} on get url: https://#{BACKEND}#{endpoint}, params: #{params}, decoder: #{decoder}"
271+
raise SerpApiError.new("HTTP request failed with response status: #{response.status} " +
272+
"reponse: #{data} on get url: https://#{BACKEND}#{endpoint}, " +
273+
"params: #{params}, decoder: #{decoder}",
274+
search_params: params,
275+
response_status: response.status,
276+
decoder: decoder)
253277
end
254278

255279
response.body

lib/serpapi/error.rb

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,77 @@
22
# frozen_string_literal: true
33

44
module SerpApi
5-
# SerpApiError wraps any errors related to the SerpApi client.
6-
class SerpApiError < StandardError
7-
# List the specific types of errors handled by the Error class.
8-
# - HTTP response errors from SerpApi.com
9-
# - Missing API key
10-
# - Credit limit
11-
# - Incorrect query
12-
# - more ...
5+
class Error < StandardError
6+
attr_reader :serpapi_error, :search_params, :response_status, :search_id, :decoder
7+
8+
# All attributes are optional keyword arguments.
9+
#
10+
# @param message [String, nil] an optional human message passed to StandardError
11+
# @param serpapi_error [String, nil] optional error string coming from SerpAPI
12+
# @param search_params [Hash, nil] optional hash of the search parameters used
13+
# @param response_status [Integer, nil] optional HTTP or response status code
14+
# @param search_id [String, nil] optional id returned by the service for the search
15+
# @param decoder [String, nil] optional decoder/format used (e.g. "json")
16+
def initialize(message = nil,
17+
serpapi_error: nil,
18+
search_params: nil,
19+
response_status: nil,
20+
search_id: nil,
21+
decoder: nil)
22+
super(message)
23+
24+
@serpapi_error = validate_optional_string(serpapi_error, :serpapi_error)
25+
@search_params = freeze_hash(search_params)
26+
@response_status = validate_optional_integer(response_status, :response_status)
27+
@search_id = validate_optional_string(search_id, :search_id)
28+
@decoder = validate_optional_string(decoder, :decoder)
29+
end
30+
31+
# Return a compact hash representation (omits nil values).
32+
#
33+
# @return [Hash]
34+
def to_h
35+
{
36+
message: message,
37+
serpapi_error: serpapi_error,
38+
search_params: search_params,
39+
response_status: response_status,
40+
search_id: search_id,
41+
decoder: decoder
42+
}.compact
43+
end
44+
45+
private
46+
47+
def validate_optional_string(value, name = nil)
48+
return nil if value.nil?
49+
unless value.is_a?(String)
50+
raise TypeError, "expected #{name || 'value'} to be a String, got #{value.class}"
51+
end
52+
53+
value.freeze
54+
end
55+
56+
def validate_optional_integer(value, name = nil)
57+
return nil if value.nil?
58+
return value if value.is_a?(Integer)
59+
60+
# Accept numeric-like strings (e.g. "200") by converting; fail otherwise.
61+
begin
62+
Integer(value)
63+
rescue ArgumentError, TypeError
64+
raise TypeError, "expected #{name || 'value'} to be an Integer (or integer-like), got #{value.inspect}"
65+
end
66+
end
67+
68+
def freeze_hash(value)
69+
return nil if value.nil?
70+
unless value.is_a?(Hash)
71+
raise TypeError, "expected search_params to be a Hash, got #{value.class}"
72+
end
73+
74+
# duplicate and freeze to avoid accidental external mutation
75+
value.dup.freeze
76+
end
1377
end
1478
end

spec/serpapi/client/client_spec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@
134134
expect { client.search(q: 'Invalid Query') }.to raise_error(SerpApi::SerpApiError)
135135
end
136136

137+
138+
137139
end
138140

139141
describe 'SerpApi client with persitency disabled' do

0 commit comments

Comments
 (0)