Class: LocoBill::Request

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

Constant Summary collapse

NON_OPERATION_WRAPPED_ACTIONS =
[:login, :logout, :getorglist]
INLINE_PARAMS_ACTIONS =
{:get_list => :object}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(action, params = {}) ⇒ Request

Returns a new instance of Request.



7
8
9
10
11
12
13
# File 'lib/loco_bill/request.rb', line 7

def initialize(action, params={})
  @action = action.to_sym
  
  
  @params = params
  @result = process_response(api(build_request_xml))
end

Instance Attribute Details

#resultObject (readonly)

Returns the value of attribute result.



3
4
5
# File 'lib/loco_bill/request.rb', line 3

def result
  @result
end

Instance Method Details

#action_xml(xml) ⇒ Object

called from build_request_xml, just the xml related to the action we’re doing TODO: refactor into a method that takes a wrapper &block



39
40
41
42
43
44
# File 'lib/loco_bill/request.rb', line 39

def action_xml(xml)
  inline_params = INLINE_PARAMS_ACTIONS.has_key?(@action) ? {INLINE_PARAMS_ACTIONS[@action] => @params[INLINE_PARAMS_ACTIONS[@action]]} : nil
  xml.send(@action, inline_params) do
    params_xml(xml, @params)
  end
end

#api(xml_body) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/loco_bill/request.rb', line 75

def api(xml_body)
  puts "XML BODY: #{xml_body}" if LocoBill::Configuration.debug
  https = Net::HTTP.new(LocoBill::Configuration.server, 443)
  https.use_ssl = true
  https.read_timeout=5 # 5 second timeout
  https.verify_mode = OpenSSL::SSL::VERIFY_NONE
  req = Net::HTTP::Post.new(LocoBill::Configuration.api_endpoint)
  req.body = "request=#{req.send(:urlencode, xml_body)}" # prefixing the request with "request=" showed up in the docs absolutely nowhere. thanks, bill.com.
  req.content_type = 'application/x-www-form-urlencoded'
  
  resp = https.start {|http|
    http.request(req)
  }
  case resp
  when Net::HTTPSuccess
    [xml_body, resp.body]
  else
    resp.error!
  end
end

#auto_loginObject

auto-login & grab session id if we don’t already have one.



16
17
18
19
20
# File 'lib/loco_bill/request.rb', line 16

def 
  if LocoBill::Configuration.session_id.nil? && @action != :login
    LocoBill::Configuration.session_id = LocoBill..result.sessionId
  end
end

#build_request_xmlObject



22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/loco_bill/request.rb', line 22

def build_request_xml
  operation_wrapped = !NON_OPERATION_WRAPPED_ACTIONS.include?(@action)
  
  Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
    xml.request(:version => LocoBill::Configuration::API_VERSION, :applicationkey => LocoBill::Configuration.application_key) {
      if operation_wrapped
        # gotta wrap most of the api calls in an "operation" block with a transaction id & session id
        xml.operation(:transactionId => LocoBill::Configuration.transaction_id, :sessionId => LocoBill::Configuration.session_id) { action_xml(xml) }
      else
        action_xml(xml)
      end
    }
  end.to_xml
end

#params_xml(xml, params) ⇒ Object

recursive, so you can do things like: BillApi.create_vendor :vendor => => ‘Test’



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/loco_bill/request.rb', line 48

def params_xml(xml, params)
  params.each do |k, v|
    k = 'id_' if k.to_s == 'id' # nokogiri blows up if we try to call 'id', but they have this workaround
    xml.send(k) {
      if v.is_a?(Hash)
        params_xml(xml, v)
      elsif v.is_a?(Array)
        v.each do |hash|
          params_xml(xml, hash)
        end
      else
        if v.is_a?(Time) || v.is_a?(Date)
          v = v.to_time.strftime("%m/%d/%Y")
        end
        
        xml.text(v)
      end
    }
  end
end

#process_response(arr) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/loco_bill/request.rb', line 96

def process_response(arr)
  original_request, raw_response = arr
  
  puts "RAW RESPONSE: #{raw_response}" if LocoBill::Configuration.debug
  res = Hash.from_xml(raw_response)['response']
  res = res["#{@action}result"] || res["operationresult"]
  res.symbolize_keys!
  
  if res[:status] == 'OK'
    RequestResult.new(res)
  elsif res[:loginresult] && res[:loginresult]["status"] == "failed" && res[:loginresult]["errormessage"] == "Invalid Session"
    # stale session, relogin + try again
    LocoBill::Configuration.session_id = LocoBill..result.sessionId
    api(original_request)
  else
    HoptoadNotifier.notify(
      :error_class   => "LocoBill::RequestError",
      :error_message => "#{res[:errorcode]}: #{res[:errormessage]}",
      :parameters    => {:raw_response => raw_response, :original_request => original_request}
    ) if Object.const_defined?(:HoptoadNotifier)
    
    raise RequestError.new "#{res[:errorcode]}: #{res[:errormessage]}"
  end
end

#transaction_idObject

generate a transaction id for operation requests



70
71
72
73
# File 'lib/loco_bill/request.rb', line 70

def transaction_id
  ::Configuration.transaction_id += 1
  "Transaction#{::Configuration.transaction_id}"
end