Class: BorrowDirect::Request

Inherits:
Object
  • Object
show all
Defined in:
lib/borrow_direct/request.rb

Overview

Generic abstract BD request, put in a Hash request body, get back a Hash answer.

response_hash = Request.new("/path/to/endpoint").request(request_hash)

Typically, clients will use various sub-classes of Request implementing calling of individual BD API’s

## AuthenticationID’s

Some API endpoints require an “AId”/“AuthencationID”. BorrowDirect::Request provides some facilities for managing obtaining such (using Authentication API), usually will be used under the hood by Request subclasses.

# fetch new auth ID using Authentication API, store it
# in self.auth_id
request.fetch_auth_id!(barcode, library_symbol)  

# return the existing value in self.auth_id, or if
# nil run fetch_auth_id! to fill it out. 
request.need_auth_id(barcode, library_symbol)

request.auth_id # cached or nil

Direct Known Subclasses

Authentication, FindItem, RequestItem, RequestQuery

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ Request

Returns a new instance of Request.



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/borrow_direct/request.rb', line 46

def initialize(path)
  @api_base = Defaults.api_base
  @api_path = path

  @api_uri = @api_base.chomp("/") + @api_path

  @expected_error_codes = []

  @timeout = Defaults.timeout
  @http_method = :post
end

Instance Attribute Details

#auth_idObject

Returns the value of attribute auth_id.



34
35
36
# File 'lib/borrow_direct/request.rb', line 34

def auth_id
  @auth_id
end

#expected_error_codesObject

Usually an error code from the server will be turned into an exception. But if there are error codes you expect (usually fixed in a subclass of Request), fill them in this array, and the responses will be returned anyway – warning, REGARDLESS of HTTP response status code, as these are often non-200 but we want to catch em anyway.



44
45
46
# File 'lib/borrow_direct/request.rb', line 44

def expected_error_codes
  @expected_error_codes
end

#http_clientObject



119
120
121
# File 'lib/borrow_direct/request.rb', line 119

def http_client
  @http_client ||= make_http_client!
end

#http_methodObject

default :post, but can be set to :get, usually by a subclass



36
37
38
# File 'lib/borrow_direct/request.rb', line 36

def http_method
  @http_method
end

#last_request_jsonObject (readonly)

Returns the value of attribute last_request_json.



37
38
39
# File 'lib/borrow_direct/request.rb', line 37

def last_request_json
  @last_request_json
end

#last_request_responseObject (readonly)

Returns the value of attribute last_request_response.



37
38
39
# File 'lib/borrow_direct/request.rb', line 37

def last_request_response
  @last_request_response
end

#last_request_timeObject (readonly)

Returns the value of attribute last_request_time.



37
38
39
# File 'lib/borrow_direct/request.rb', line 37

def last_request_time
  @last_request_time
end

#last_request_uriObject (readonly)

Returns the value of attribute last_request_uri.



37
38
39
# File 'lib/borrow_direct/request.rb', line 37

def last_request_uri
  @last_request_uri
end

#timeoutObject

Returns the value of attribute timeout.



33
34
35
# File 'lib/borrow_direct/request.rb', line 33

def timeout
  @timeout
end

Instance Method Details

#fetch_auth_id!(barcode, library_symbol) ⇒ Object

Fetches new authID, stores it in self.auth_id, overwriting any previous value there. Will raise BorrowDirect::Error if no auth could be fetched.

returns auth_id too.



143
144
145
146
147
148
149
# File 'lib/borrow_direct/request.rb', line 143

def fetch_auth_id!(barcode, library_symbol)
  auth = Authentication.new(barcode, library_symbol)
  # use the same HTTPClient so we use the same HTTP connection, perhaps
  # slightly more performance worth a shot. 
  auth.http_client = http_client
  self.auth_id = auth.get_auth_id
end

#need_auth_id(barcode, library_symbol) ⇒ Object

Will use value in self.auth_id, or if nil will fetch a value with fetch_auth_id! and return that.



153
154
155
# File 'lib/borrow_direct/request.rb', line 153

def need_auth_id(barcode, library_symbol)
  self.auth_id || fetch_auth_id!(barcode, library_symbol)
end

#request(hash, aid = nil) ⇒ Object

First param is request hash, will be query param for GET or JSON body for POST Second param is optional AuthenticationID used by BD system – if given, will be added to URI as “?aid=$AID”, even for POST. Yep, that’s Relais documented protocol eg relais.atlassian.net/wiki/display/ILL/Find+Item



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/borrow_direct/request.rb', line 62

def request(hash, aid = nil)
  http = http_client
  
  uri = @api_uri
  if aid
    uri += "?aid=#{CGI.escape aid}"
  end

  # Mostly for debugging, store these
  @last_request_uri = uri

  start_time = Time.now

  if self.http_method == :post
    @last_request_json = json_request = JSON.generate(hash)        
    http_response = http.post uri, json_request, self.request_headers
  elsif self.http_method == :get
    @last_request_query_params = hash
    http_response = http.get uri, hash, self.request_headers
  else
    raise ArgumentError.new("BorrowDirect::Request only understands http_method :get and :post, not `#{self.http_method}`")
  end

  @last_request_response = http_response
  @last_request_time     = Time.now - start_time

  response_hash = begin
    JSON.parse(http_response.body)
  rescue JSON::ParserError => json_parse_exception
    nil
  end

  # will be nil if we have none
  einfo = error_info(response_hash)
  expected_error = (einfo && self.expected_error_codes.include?(einfo.number))


  if einfo && (! expected_error)
    if BorrowDirect::Error.invalid_aid_code?(einfo.number)
      raise BorrowDirect::InvalidAidError.new(einfo.message, einfo.number, aid)
    else
      raise BorrowDirect::Error.new(einfo.message, einfo.number)      
    end
  elsif http_response.code != 200 && (! expected_error)
    raise BorrowDirect::HttpError.new("HTTP Error: #{http_response.code}: #{http_response.body}")
  elsif response_hash.nil?
    raise BorrowDirect::Error.new("Could not parse expected JSON response: #{http_response.code} #{json_parse_exception}: #{http_response.body}")
  end

  

  return response_hash
rescue HTTPClient::ReceiveTimeoutError, HTTPClient::ConnectTimeoutError, HTTPClient::SendTimeoutError => e
  elapsed = Time.now - start_time
  raise BorrowDirect::HttpTimeoutError.new("Timeout after #{elapsed.round(1)}s connecting to BorrowDirect server at #{@api_base}", self.timeout)
end

#request_headersObject

For now, we can send same request headers for all requests. May have to make parameterized later. Note SOME but not all BD API endpoints REQUIRE User-Agent and Accept-Language (for no discernable reason)

NOTE WELL: API sometimes requires User-Agent _not to change_ when using an AuthorizationID, or it will revoke your authorization. Need to use the same User-Agent when using an auth_id as you used when receiving it.



131
132
133
134
135
136
# File 'lib/borrow_direct/request.rb', line 131

def request_headers
  { "Content-Type" => "application/json", 
    "User-Agent" => "ruby borrow_direct gem #{BorrowDirect::VERSION} (HTTPClient #{HTTPClient::VERSION}) https://github.com/jrochkind/borrow_direct", 
    "Accept-Language" => "en"
  }
end

#with_auth_id(aid) ⇒ Object

Can be used to set an already existing AuthID to be used. Beware, we have no facility for rescuing from escpired auth ids at the moment.



160
161
162
163
# File 'lib/borrow_direct/request.rb', line 160

def with_auth_id(aid)
  self.auth_id = aid
  return self
end