Class: SearchGsa::Service

Inherits:
Common::Client::Base show all
Includes:
Common::Client::Concerns::Monitoring
Defined in:
lib/search_gsa/service.rb

Overview

This class builds a wrapper around api.gsa.gov web results API. Creating a new instance of class will and calling #results will return a ResultsResponse upon success or an exception upon failure.

Constant Summary collapse

STATSD_KEY_PREFIX =
'api.search'

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Common::Client::Concerns::Monitoring

#increment, #increment_failure, #increment_total, #with_monitoring

Methods inherited from Common::Client::Base

#config, configuration, #connection, #delete, #get, #perform, #post, #put, #raise_backend_exception, #raise_not_authenticated, #request, #sanitize_headers!, #service_name

Methods included from SentryLogging

#log_exception_to_sentry, #log_message_to_sentry, #non_nil_hash?, #normalize_level, #rails_logger, #set_sentry_metadata

Constructor Details

#initialize(query, page = 1) ⇒ Service

Returns a new instance of Service.



24
25
26
27
# File 'lib/search_gsa/service.rb', line 24

def initialize(query, page = 1)
  @query = query
  @page = page.to_i
end

Instance Attribute Details

#pageObject (readonly)

Returns the value of attribute page.



22
23
24
# File 'lib/search_gsa/service.rb', line 22

def page
  @page
end

#queryObject (readonly)

Returns the value of attribute query.



22
23
24
# File 'lib/search_gsa/service.rb', line 22

def query
  @query
end

Instance Method Details

#access_keyObject (private)



66
67
68
# File 'lib/search_gsa/service.rb', line 66

def access_key
  Settings.search.access_key
end

#affiliateObject (private)



62
63
64
# File 'lib/search_gsa/service.rb', line 62

def affiliate
  Settings.search.affiliate
end

#error_code_name(error_status) ⇒ Object (private)



129
130
131
# File 'lib/search_gsa/service.rb', line 129

def error_code_name(error_status)
  "SEARCH_GSA_#{error_status}"
end

#handle_429!(error) ⇒ Object (private)



113
114
115
116
117
118
# File 'lib/search_gsa/service.rb', line 113

def handle_429!(error)
  return unless error.status == 429

  StatsD.increment("#{SearchGsa::Service::STATSD_KEY_PREFIX}.exceptions", tags: ['exception:429'])
  raise_backend_exception(error_code_name(error.status), self.class, error)
end

#handle_error(error) ⇒ Object (private)



87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/search_gsa/service.rb', line 87

def handle_error(error)
  case error
  when Common::Client::Errors::ClientError
    # Handle upstream 5xx errors first since the structure of those errors usually isn't the same.
    handle_server_error!(error)
    message = parse_messages(error).first
    save_error_details(message)
    handle_429!(error)
    raise_backend_exception(error_code_name(400), self.class, error) if error.status >= 400
  else
    raise error
  end
end

#handle_server_error!(error) ⇒ Object (private)



120
121
122
123
124
125
126
127
# File 'lib/search_gsa/service.rb', line 120

def handle_server_error!(error)
  return unless [503, 504].include?(error.status)

  # Catch when the error's structure doesn't match what's usually expected.
  message = error.body.is_a?(Hash) ? parse_messages(error).first : 'SearchGSA API is down'
  save_error_details(message)
  raise_backend_exception(error_code_name(error.status), self.class, error)
end

#limitObject (private)



83
84
85
# File 'lib/search_gsa/service.rb', line 83

def limit
  Search::Pagination::ENTRIES_PER_PAGE
end

#offsetObject (private)

Calculate the offset parameter based on the requested page number



72
73
74
75
76
77
78
79
80
81
# File 'lib/search_gsa/service.rb', line 72

def offset
  if page <= 1
    # We want first page of results
    0
  else
    # Max offset for search API is 999
    # If there are 20 results and the user requests page 3, there will be an empty result set
    [((page - 1) * limit), 999].min
  end
end

#parse_messages(error) ⇒ Object (private)



101
102
103
# File 'lib/search_gsa/service.rb', line 101

def parse_messages(error)
  error.body&.dig('errors')
end

#query_paramsObject (private)

Required params [affiliate, access_key, query] Optional params [enable_highlighting, limit, offset, sort_by]



52
53
54
55
56
57
58
59
60
# File 'lib/search_gsa/service.rb', line 52

def query_params
  {
    affiliate:,
    access_key:,
    query:,
    offset:,
    limit:
  }
end

#resultsObject

GETs a list of search results from api.gsa,gov API @return

Search::ResultsResponse

wrapper around results data



32
33
34
35
36
37
38
39
# File 'lib/search_gsa/service.rb', line 32

def results
  with_monitoring do
    response = perform(:get, results_url, query_params)
    Search::ResultsResponse.from(response)
  end
rescue => e
  handle_error(e)
end

#results_urlObject (private)



43
44
45
# File 'lib/search_gsa/service.rb', line 43

def results_url
  config.base_path
end

#save_error_details(error_message) ⇒ Object (private)



105
106
107
108
109
110
111
# File 'lib/search_gsa/service.rb', line 105

def save_error_details(error_message)
  Sentry.set_extras(
    message: error_message,
    url: config.base_path
  )
  Sentry.set_tags(search: 'general_search_query_error')
end