Skip to content

Commit

Permalink
Initial refactor for JSON support
Browse files Browse the repository at this point in the history
  • Loading branch information
minimul committed Aug 6, 2015
1 parent 22dc453 commit b7a5f74
Show file tree
Hide file tree
Showing 7 changed files with 381 additions and 174 deletions.
5 changes: 4 additions & 1 deletion lib/quickbooks-ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
require 'quickbooks/model/definition'
require 'quickbooks/model/validator'
require 'quickbooks/model/base_model'
require 'quickbooks/model/base_model_json'
require 'quickbooks/model/base_reference'
require 'quickbooks/model/document_numbering'
require 'quickbooks/model/global_tax_calculation'
Expand Down Expand Up @@ -112,7 +113,9 @@

#== Services
require 'quickbooks/service/service_crud'
require 'quickbooks/service/service_crud_json'
require 'quickbooks/service/base_service'
require 'quickbooks/service/base_service_json'
require 'quickbooks/service/access_token'
require 'quickbooks/service/class'
require 'quickbooks/service/attachable'
Expand Down Expand Up @@ -209,7 +212,7 @@ class ServiceUnavailable < StandardError; end
class MissingRealmError < StandardError; end

class IntuitRequestException < StandardError
attr_accessor :message, :code, :detail, :type, :request_xml
attr_accessor :message, :code, :detail, :type, :request_xml, :request_json
def initialize(msg)
self.message = msg
super(msg)
Expand Down
26 changes: 26 additions & 0 deletions lib/quickbooks/model/base_model_json.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Quickbooks
module Model
class BaseModelJSON
include Definition
include ActiveModel::Validations
include Validator
include ROXML

xml_convention :camelcase

def initialize(attributes={})
attributes.each {|key, value| public_send("#{key}=", value) }
end

def to_json
params = {}
attributes.each_pair do |k, v|
next if v.blank?
params[k.camelize] = v.is_a?(Array) ? v.inject([]){|mem, item| mem << item.to_json; mem} : v
end
params.to_json
end

end
end
end
11 changes: 3 additions & 8 deletions lib/quickbooks/model/tax_service.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Quickbooks
module Model
class TaxService < BaseModel
class TaxService < BaseModelJSON

xml_accessor :tax_code_id, :from => "TaxCodeId"
xml_accessor :tax_code, :from => "TaxCode"
Expand All @@ -15,13 +15,8 @@ def initialize(options = {})
super
end

def to_json
params = {}
attributes.each_pair do |k, v|
next if v.blank?
params[k.camelize] = v.is_a?(Array) ? v.inject([]){|mem, item| mem << item.to_json; mem} : v
end
params.to_json
def model
Quickbooks::Model::TaxService
end

def self.from_json(response)
Expand Down
39 changes: 32 additions & 7 deletions lib/quickbooks/service/base_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ def url_for_base
"#{@base_uri}/#{@company_id}"
end

def is_json?
HTTP_CONTENT_TYPE == "application/json"
end

def default_model_query
"SELECT * FROM #{self.class.name.split("::").last}"
end
Expand Down Expand Up @@ -208,8 +212,7 @@ def do_http(method, url, body, headers) # throws IntuitRequestException
log "------ QUICKBOOKS-RUBY REQUEST ------"
log "METHOD = #{method}"
log "RESOURCE = #{url}"
log "REQUEST BODY:"
log(log_xml(body))
log_request_body(body)
log "REQUEST HEADERS = #{headers.inspect}"

response = case method
Expand All @@ -222,7 +225,7 @@ def do_http(method, url, body, headers) # throws IntuitRequestException
else
raise "Do not know how to perform that HTTP operation"
end
check_response(response, :request_xml => body)
check_response(response, :request => body)
end

def add_query_string_to_url(url, params)
Expand All @@ -236,9 +239,7 @@ def add_query_string_to_url(url, params)
def check_response(response, options = {})
log "------ QUICKBOOKS-RUBY RESPONSE ------"
log "RESPONSE CODE = #{response.code}"
log "RESPONSE BODY:"
log(log_xml(response.plain_body))
parse_xml(response.plain_body)
log_response_body(response)
status = response.code.to_i
case status
when 200
Expand All @@ -263,13 +264,37 @@ def check_response(response, options = {})
end
end

def log_response_body(response)
log "RESPONSE BODY:"
if is_json?
log ">>>>#{response.plain_body.inspect}"
parse_json(response.plain_body)
else
log(log_xml(response.plain_body))
parse_xml(response.plain_body)
end
end

def log_request_body(body)
log "REQUEST BODY:"
if is_json?
log(body.inspect)
else
log(log_xml(body))
end
end

def parse_and_raise_exception(options = {})
err = parse_intuit_error
ex = Quickbooks::IntuitRequestException.new("#{err[:message]}:\n\t#{err[:detail]}")
ex.code = err[:code]
ex.detail = err[:detail]
ex.type = err[:type]
ex.request_xml = options[:request_xml]
if is_json?
ex.request_json = options[:request]
else
ex.request_xml = options[:request]
end
raise ex
end

Expand Down
140 changes: 140 additions & 0 deletions lib/quickbooks/service/base_service_json.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
module Quickbooks
module Service
class BaseServiceJSON < BaseService
include ServiceCrudJSON
HTTP_CONTENT_TYPE = 'application/json'
HTTP_ACCEPT = 'application/json'
attr_reader :last_response_json

def url_for_resource(resource)
"#{url_for_base}/#{resource}"
end

def url_for_base
raise MissingRealmError.new unless @company_id
"#{@base_uri}/#{@company_id}"
end

def default_model_query
"SELECT * FROM #{self.class.name.split("::").last}"
end

def url_for_query(query = nil, start_position = 1, max_results = 20)
query ||= default_model_query
query = "#{query} STARTPOSITION #{start_position} MAXRESULTS #{max_results}"

"#{url_for_base}/query?query=#{URI.encode_www_form_component(query)}"
end

private

def parse_json(json)
@last_response_json = json
end

def do_http(method, url, body, headers) # throws IntuitRequestException
if @oauth.nil?
raise "OAuth client has not been initialized. Initialize with setter access_token="
end
unless headers.has_key?('Content-Type')
headers['Content-Type'] = HTTP_CONTENT_TYPE
end
unless headers.has_key?('Accept')
headers['Accept'] = HTTP_ACCEPT
end
unless headers.has_key?('Accept-Encoding')
headers['Accept-Encoding'] = HTTP_ACCEPT_ENCODING
end

log "------ QUICKBOOKS-RUBY REQUEST ------"
log "METHOD = #{method}"
log "RESOURCE = #{url}"
log "REQUEST BODY:"
log(log_xml(body))
log "REQUEST HEADERS = #{headers.inspect}"

response = case method
when :get
@oauth.get(url, headers)
when :post
@oauth.post(url, body, headers)
when :upload
@oauth.post_with_multipart(url, body, headers)
else
raise "Do not know how to perform that HTTP operation"
end
check_response(response, :request_xml => body)
end

def add_query_string_to_url(url, params)
if params.is_a?(Hash) && !params.empty?
url + "?" + params.collect { |k| "#{k.first}=#{k.last}" }.join("&")
else
url
end
end

def check_response(response, options = {})
log "------ QUICKBOOKS-RUBY RESPONSE ------"
log "RESPONSE CODE = #{response.code}"
log "RESPONSE BODY:"
log(log_xml(response.plain_body))
parse_xml(response.plain_body)
status = response.code.to_i
case status
when 200
# even HTTP 200 can contain an error, so we always have to peek for an Error
if response_is_error?
parse_and_raise_exception(options)
else
response
end
when 302
raise "Unhandled HTTP Redirect"
when 401
raise Quickbooks::AuthorizationFailure
when 403
raise Quickbooks::Forbidden
when 400, 500
parse_and_raise_exception(options)
when 503, 504
raise Quickbooks::ServiceUnavailable
else
raise "HTTP Error Code: #{status}, Msg: #{response.plain_body}"
end
end

def parse_and_raise_exception(options = {})
err = parse_intuit_error
ex = Quickbooks::IntuitRequestException.new("#{err[:message]}:\n\t#{err[:detail]}")
ex.code = err[:code]
ex.detail = err[:detail]
ex.type = err[:type]
ex.request_xml = options[:request_xml]
raise ex
end

def response_is_error?
@last_response_json['Fault'].present?
end

def parse_intuit_error
error = {:message => "", :detail => "", :type => nil, :code => 0}
fault = @last_response_json['Fault']
if fault.present?
error[:type] = fault['type'] if fault.has_key?('type')
if fault_error = fault['Error'].first
error[:message] = fault_error['Message']
error[:detail] = fault_error['Detail']
error[:code] = fault_error['code']
end
end
error
rescue Exception => exception
error[:detail] = @last_response_json.to_s
error
end

end
end
end
18 changes: 18 additions & 0 deletions lib/quickbooks/service/service_crud_json.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Quickbooks
module Service
module ServiceCrudJSON

def create(entity, options = {})
raise Quickbooks::InvalidModelException.new(entity.errors.full_messages.join(',')) unless entity.valid?
response = do_http(:post, url_for_resource, entity.to_json, options)
if response.code.to_i == 200
response.plain_body
else
nil
end
end

end
end
end

Loading

0 comments on commit b7a5f74

Please sign in to comment.