class RecombeeApiClient::RecombeeClient

Client for sending requests to Recombee recommender system

Constants

BATCH_MAX_SIZE
USER_AGENT

Public Class Methods

new(account, token, options = {}) click to toggle source
  • account -> Name of your account at Recombee

  • token -> Secret token obtained from Recombee for signing requests

# File lib/recombee_api_client.rb, line 26
def initialize(account, token, options = {})
  @account = account
  @token = token
  @protocol = options[:protocol] || 'https'
  @base_uri = get_base_uri(options)
end

Public Instance Methods

send(request) click to toggle source
  • request -> ApiRequest to be sent to Recombee recommender

# File lib/recombee_api_client.rb, line 35
def send(request)

  return send_multipart_batch(request) if request.kind_of? Batch and request.requests.size > BATCH_MAX_SIZE

  timeout = request.timeout / 1000
  uri = process_request_uri(request)
  uri = sign_url(uri)
  protocol = request.ensure_https ? 'https' : @protocol.to_s
  uri = protocol + '://' + @base_uri + uri
  # puts uri
  begin
    case request.method
    when :put
      put(request, uri, timeout)
    when :get
      get(request, uri, timeout)
    when :post
      post(request, uri, timeout)
    when :delete
      delete(request, uri, timeout)
    end
  rescue Timeout::Error
    fail ApiTimeout.new(request)
  end
end

Private Instance Methods

check_errors(response, request) click to toggle source
# File lib/recombee_api_client.rb, line 124
def check_errors(response, request)
  status_code = response.code
  return if status_code == 200 || status_code == 201
  fail ResponseError.new(request, status_code, response.body)
end
delete(request, uri, timeout) click to toggle source
# File lib/recombee_api_client.rb, line 112
def delete(request, uri, timeout)
  response = self.class.delete(uri, body: request.body_parameters.to_json,
                               headers: { 'Content-Type' => 'application/json' }.merge(USER_AGENT),
                               timeout: timeout)
  check_errors(response, request)
  begin
    return JSON.parse(response.body)
  rescue JSON::ParserError
    return response.body
  end
end
format_query_parameter_value(value) click to toggle source
# File lib/recombee_api_client.rb, line 152
def format_query_parameter_value(value)
  return CGI::escape("#{value}") unless value.kind_of?(Array)
  value.map{|v| CGI::escape("#{v}")}.join(',')
end
get(request, uri, timeout) click to toggle source
# File lib/recombee_api_client.rb, line 94
def get(request, uri, timeout)
  response = self.class.get(uri, timeout: timeout, headers: USER_AGENT)
  check_errors(response, request)
  JSON.parse(response.body)
end
get_base_uri(options) click to toggle source
# File lib/recombee_api_client.rb, line 74
def get_base_uri(options)
  base_uri = ENV['RAPI_URI']
  base_uri ||= options[:base_uri]

  if options.key? :region
    raise ArgumentError.new(':base_uri and :region cannot be specified at the same time') if base_uri
    base_uri = get_regional_base_uri(options[:region])
  end
  base_uri||= 'rapi.recombee.com'
  base_uri
end
get_regional_base_uri(region) click to toggle source
# File lib/recombee_api_client.rb, line 63
def get_regional_base_uri(region)
  uri = {
        'ap-se' => 'rapi-ap-se.recombee.com',
        'ca-east' => 'rapi-ca-east.recombee.com',
        'eu-west' => 'rapi-eu-west.recombee.com',
        'us-west' => 'rapi-us-west.recombee.com'
      }[region.to_s.gsub('_', '-').downcase]
  raise ArgumentError.new("Region \"#{region}\" is unknown. You may need to update the version of the SDK.") if uri == nil
  uri
end
hmac_sign(uri, time) click to toggle source
# File lib/recombee_api_client.rb, line 172
def hmac_sign(uri, time)
  url = uri + time
  digest = OpenSSL::Digest.new('sha1')
  OpenSSL::HMAC.hexdigest(digest, @token, url)
end
hmac_time(uri) click to toggle source
# File lib/recombee_api_client.rb, line 167
def hmac_time(uri)
  res = (uri.include? '?') ? '&' : '?'
  res << "hmac_timestamp=#{Time.now.utc.to_i}"
end
post(request, uri, timeout) click to toggle source
# File lib/recombee_api_client.rb, line 100
def post(request, uri, timeout)
  response = self.class.post(uri, body: request.body_parameters.to_json, 
                             headers: { 'Content-Type' => 'application/json' }.merge(USER_AGENT),
                             timeout: timeout)
  check_errors(response, request)
  begin
    return JSON.parse(response.body)
  rescue JSON::ParserError
    return response.body
  end
end
process_request_uri(request) click to toggle source
# File lib/recombee_api_client.rb, line 136
def process_request_uri(request)
  uri = request.path
  uri.slice! ('/{databaseId}/')
  uri += query_parameters_to_url(request)
  uri
end
put(request, uri, timeout) click to toggle source
# File lib/recombee_api_client.rb, line 86
def put(request, uri, timeout)
  response = self.class.put(uri, body: request.body_parameters.to_json, 
                            headers: { 'Content-Type' => 'application/json' }.merge(USER_AGENT),
                            timeout: timeout)
  check_errors(response, request)
  response.body
end
query_parameters_to_url(req) click to toggle source
# File lib/recombee_api_client.rb, line 143
def query_parameters_to_url(req)
  ps = ''
  req.query_parameters.each do |name, val|
    ps += (ps.include? '?') ? '&' : '?'
    ps += "#{name}=#{format_query_parameter_value(val)}"
  end
  ps
end
send_multipart_batch(request) click to toggle source
# File lib/recombee_api_client.rb, line 130
def send_multipart_batch(request)
  requests_parts = request.requests.each_slice(BATCH_MAX_SIZE)
  responses = requests_parts.map {|rqs| Batch.new(rqs)}.map{|batch| send(batch)}
  responses.inject([]){|result,resp| result + resp}
end
sign_url(req_part) click to toggle source

Sign request with HMAC, request URI must be exacly the same We have 30s to complete request with this token

# File lib/recombee_api_client.rb, line 159
def sign_url(req_part)
  uri = "/#{@account}/#{req_part}"
  time = hmac_time(uri)
  sign = hmac_sign(uri, time)
  res = uri + time + "&hmac_sign=#{sign}"
  res
end