class Booker::Client

Constants

ACCESS_TOKEN_ENDPOINT
ACCESS_TOKEN_HTTP_METHOD
TimeZone

Attributes

base_url[RW]
client_id[RW]
client_secret[RW]
temp_access_token[RW]
temp_access_token_expires_at[RW]
token_store[RW]
token_store_callback_method[RW]

Public Class Methods

new(options = {}) click to toggle source
# File lib/booker/client.rb, line 10
def initialize(options = {})
  options.each { |key, value| send(:"#{key}=", value) }
  self.base_url ||= get_base_url
  self.client_id ||= ENV['BOOKER_CLIENT_ID']
  self.client_secret ||= ENV['BOOKER_CLIENT_SECRET']
end

Public Instance Methods

access_token() click to toggle source
# File lib/booker/client.rb, line 126
def access_token
  (self.temp_access_token && !temp_access_token_expired?) ? self.temp_access_token : get_access_token
end
access_token_options() click to toggle source
# File lib/booker/client.rb, line 130
def access_token_options
  {
      client_id: self.client_id,
      client_secret: self.client_secret
  }
end
access_token_response(http_options) click to toggle source
# File lib/booker/client.rb, line 165
def access_token_response(http_options)
  send(self.class::ACCESS_TOKEN_HTTP_METHOD, self.class::ACCESS_TOKEN_ENDPOINT, http_options, nil)
end
full_url(path) click to toggle source
# File lib/booker/client.rb, line 102
def full_url(path)
  uri = URI(path)
  uri.scheme ? path : "#{self.base_url}#{path}"
end
get(path, params, booker_model=nil) click to toggle source
# File lib/booker/client.rb, line 27
def get(path, params, booker_model=nil)
  booker_resources = get_booker_resources(:get, path, params, nil, booker_model)

  build_resources(booker_resources, booker_model)
end
get_access_token() click to toggle source
# File lib/booker/client.rb, line 143
def get_access_token
  http_options = access_token_options
  response = raise_invalid_api_credentials_for_empty_resp! { access_token_response(http_options) }

  self.temp_access_token_expires_at = Time.now + response['expires_in'].to_i.seconds
  self.temp_access_token = response['access_token']

  update_token_store

  self.temp_access_token
end
get_base_url() click to toggle source
# File lib/booker/client.rb, line 17
def get_base_url
  env_key = try(:env_base_url_key)

  if env_key.present?
    ENV[env_key] || try(:default_base_url)
  else
    try(:default_base_url)
  end
end
get_booker_resources(http_method, path, params=nil, body=nil, booker_model=nil) click to toggle source
# File lib/booker/client.rb, line 78
def get_booker_resources(http_method, path, params=nil, body=nil, booker_model=nil)
  http_options = request_options(params, body)
  http_options[:verify] = false # This should be removed so SSL can be verified
  url = full_url(path)
  puts "BOOKER REQUEST: #{http_method} #{url} #{http_options}" if ENV['BOOKER_API_DEBUG'] == 'true'

  # Allow it to retry the first time unless it is an authorization error
  begin
    booker_resources = handle_errors!(url, http_options, HTTParty.send(http_method, url, http_options))
  rescue Booker::Error, Net::ReadTimeout => ex
    if ex.is_a? Booker::InvalidApiCredentials
      raise ex
    else
      sleep 1
      booker_resources = nil
    end
  end

  return results_from_response(booker_resources, booker_model) if booker_resources.present?
  booker_resources = handle_errors!(url, http_options, HTTParty.send(http_method, url, http_options))
  return results_from_response(booker_resources, booker_model) if booker_resources.present?
  raise Booker::Error.new(url: url, request: http_options, response: booker_resources)
end
handle_errors!(url, request, response) click to toggle source
# File lib/booker/client.rb, line 107
def handle_errors!(url, request, response)
  puts "BOOKER RESPONSE: #{response}" if ENV['BOOKER_API_DEBUG'] == 'true'

  ex = Booker::Error.new(url: url, request: request, response: response)
  if ex.error.present? || !response.success?
    case ex.error
      when 'invalid_client'
        raise Booker::InvalidApiCredentials.new(url: url, request: request, response: response)
      # when 'invalid access token'
      #   get_access_token
      #   return nil
      else
        raise ex
    end
  end

  response
end
paginated_request(method:, path:, params:, model: nil, fetched: [], fetch_all: true) click to toggle source
# File lib/booker/client.rb, line 45
def paginated_request(method:, path:, params:, model: nil, fetched: [], fetch_all: true)
  page_size = params['PageSize']
  page_number = params['PageNumber']

  if page_size.nil? || page_size < 1 || page_number.nil? || page_number < 1 || !params['UsePaging']
    raise ArgumentError, 'params must include valid PageSize, PageNumber and UsePaging'
  end

  puts "fetching #{path} with #{params.except('access_token')}. #{fetched.length} results so far."

  results = self.send(method, path, params, model)

  unless results.is_a?(Array)
    raise StandardError, "Result from paginated request to #{path} with params: #{params} is not a collection"
  end

  fetched.concat(results)
  results_length = results.length

  if fetch_all
    if results_length > 0
      # TODO (#111186744): Add logging to see if any pages with less than expected data (as seen in the /appointments endpoint)
      new_params = params.deep_dup
      new_params['PageNumber'] = page_number + 1
      paginated_request(method: method, path: path, params: new_params, model: model, fetched: fetched)
    else
      fetched
    end
  else
    results
  end
end
post(path, data, booker_model=nil) click to toggle source
# File lib/booker/client.rb, line 33
def post(path, data, booker_model=nil)
  booker_resources = get_booker_resources(:post, path, nil, data.to_json, booker_model)

  build_resources(booker_resources, booker_model)
end
put(path, data, booker_model=nil) click to toggle source
# File lib/booker/client.rb, line 39
def put(path, data, booker_model=nil)
  booker_resources = get_booker_resources(:put, path, nil, data.to_json, booker_model)

  build_resources(booker_resources, booker_model)
end
raise_invalid_api_credentials_for_empty_resp!() { || ... } click to toggle source
# File lib/booker/client.rb, line 155
def raise_invalid_api_credentials_for_empty_resp!
  yield
rescue Booker::Error => ex
  if (response = ex.response).present?
    raise ex
  else
    raise Booker::InvalidApiCredentials.new(url: ex.url, request: ex.request, response: response)
  end
end
update_token_store() click to toggle source
# File lib/booker/client.rb, line 137
def update_token_store
  if self.token_store.present? && self.token_store_callback_method.present?
    self.token_store.send(self.token_store_callback_method, self.temp_access_token, self.temp_access_token_expires_at)
  end
end

Private Instance Methods

build_resources(resources, booker_model) click to toggle source
# File lib/booker/client.rb, line 183
def build_resources(resources, booker_model)
  return resources if booker_model.nil?

  if resources.is_a? Hash
    booker_model.from_hash(resources)
  elsif resources.is_a? Array
    booker_model.from_list(resources)
  else
    resources
  end
end
request_options(query=nil, body=nil) click to toggle source
# File lib/booker/client.rb, line 170
def request_options(query=nil, body=nil)
  options = {
    headers: {
      'Content-Type' => 'application/json; charset=utf-8'
    },
    open_timeout: 120
  }

  options[:body] = body if body.present?
  options[:query] = query if query.present?
  options
end
results_from_response(response, booker_model=nil) click to toggle source
# File lib/booker/client.rb, line 199
def results_from_response(response, booker_model=nil)
  return response['Results'] unless response['Results'].nil?

  if booker_model
    model_name = booker_model.to_s.demodulize
    return response[model_name] unless response[model_name].nil?

    pluralized = model_name.pluralize
    return response[pluralized] unless response[pluralized].nil?
  end

  response.parsed_response if response
end
temp_access_token_expired?() click to toggle source
# File lib/booker/client.rb, line 195
def temp_access_token_expired?
  self.temp_access_token_expires_at.nil? || self.temp_access_token_expires_at <= Time.now
end