Permalink
Browse files

Fixed error handling issues. Exposed some extra methods used in Patro…

…nSaint. Added decode_search_url method.
  • Loading branch information...
1 parent e4fb88a commit b1be3d7f5de8a1dd879e8da4e3e95ab7a0e4f87d @makenai makenai committed Jun 23, 2011
View
@@ -13,22 +13,37 @@ def execute( request )
end
end
+ # Allows you to call an arbitrary endpoint if you're feeling frisky
+ def call_endpoint( method, endpoint, options={} )
+ execute( request( method, endpoint, options ) )
+ end
+
protected
# Override to provide credentials that get injected into the query string
def credentials
{}
end
+
+ # Ditto.. override to add cookies to the request
+ def cookies
+ {}
+ end
# Create a request
def request( method, endpoint, options={} )
request = Zappos::Request.new( method, @base_url, endpoint )
if options[:ssl]
request.use_ssl = true
end
+
+ # Per Jimmy, it's best to pass the key in they query params
query_params = options[:query_params] || {}
- # Per Jimmy, it's best to pass the key in they query params
request.query_params = credentials.merge( query_params )
+
+ # If we have cookies for the request, set them here
+ request.cookies = cookies
+
if options[:body_params]
request.body_params = options[:body_params]
end
@@ -78,7 +93,7 @@ def put_response( endpoint, options={} )
def delete_response( endpoint, options={} )
execute( delete( endpoint, options ) )
end
-
+
private
# Batch queries must have their parameters encoded specially
@@ -16,6 +16,51 @@ def search_facet_values( facet )
response.facets.first[:values] rescue nil
end
+ # Decodes a Zappos.com search URL into a hash for a Search API call
+ def decode_search_url( url )
+ query = {}
+ case
+ when parts = url.match(%r{^/search/([^/?]*)(?:/filter/?(.*?))?(?:/orig/(.*?))?(?:/termLander/(.*?))?(?:/page/(\d+))?(?:/sort/(.*?))?(?:/debug/(.*?))?(?:/noEncode/(true|false?))?/?(?:\?(.*))?$})
+ term = CGI::unescape( parts[1] )
+ query[:term] = term unless term == 'null'
+ query[:filters] = parse_filters( parts[2] )
+ query[:sort] = parse_sort( parts[6] )
+ query[:page] = parts[5]
+ when parts = url.match(%r{^/search/brand/(\d+)(?:/filter/?(.*?))?(?:/page/(\d+))?(?:/sort/(.*?))?(?:/debug/(.*?))?(?:/noEncode/(true|false?))?/?(?:\?(.*))?$})
+ query[:filters] = parse_filters( parts[2] ) || {}
+ query[:filters][:brandId] = [ parts[1] ]
+ query[:sort] = parse_sort( parts[6] )
+ query[:page] = parts[5]
+ when parts = url.match(%r{^/([^/?]+)$})
+ query[:term] = CGI::unescape(parts[1]).gsub(/[_-]/, ' ')
+ end
+ query.delete_if {|key,value| !value }
+ end
+
+ private
+
+ def parse_filters(filter_string)
+ return unless filter_string
+ filters = {}
+ filter_string.split('/').each_slice(2) do |key,value|
+ facet = key.to_sym
+ filters[ facet ] ||= [] << CGI::unescape( value ).gsub(/^"|"$/,'')
+ end
+ return filters
+ end
+
+ def parse_sort( sort_string )
+ return unless sort_string
+ key, value = sort_string.split('/')
+ { key.to_sym => CGI::unescape( value ) }
+ end
+
+ def parse_page( page_string )
+ return unless page_string
+ page, number = page_string.split('/')
+ return number.to_i
+ end
+
end
end
end
View
@@ -9,7 +9,7 @@ module Zappos
class Request #:nodoc:all
attr_accessor :use_ssl, :response_class
- attr_reader :query_params, :body_params
+ attr_reader :query_params, :body_params, :cookies
def initialize( method, host, path, options={} )
@method = method
@@ -39,6 +39,11 @@ def body_params=(params)
@body_params = Hashie::Mash.new( params )
end
+ # Store cookies.. ditto
+ def cookies=(cookies)
+ @cookies = Hashie::Mash.new( cookies )
+ end
+
# A combination of body and query parameters.
def params()
params = query_params.clone()
@@ -64,6 +69,16 @@ def request()
if @body_params
request.set_form_data( @body_params )
end
+ if cookies
+ cookie_pairs = []
+ cookies.each do |key,value|
+ next unless value
+ # Zappos uses this odd multi-value cookie format
+ escaped_value = value.split('&').collect { |v| CGI::escape(v) }.join('&')
+ cookie_pairs << "#{key}=#{escaped_value}"
+ end
+ request.add_field( "Cookie", cookie_pairs.join(';') ) if cookie_pairs.length > 0
+ end
request
end
View
@@ -7,21 +7,32 @@ class Response
attr_accessor :data
def initialize( client, request, response ) #:nodoc
- @client = client
- @request = request
- @response = response
- data = JSON.parse( @response.body )
+ @client = client
+ @request = request
+ @response = response
+ data = begin
+ JSON.parse( @response.body )
+ rescue JSON::ParserError
+ @parse_error = true
+ { :error => "JSON Parser Error:\n#{@response.body}" }
+ end
@data = Hashie::Mash.new( data )
end
# True if we had a successful response
def success?
- (200..299).include?( @response.code.to_i )
+ (200..299).include?( code )
+ end
+
+ # Returns true if there was a parse error
+ def parse_error?
+ @parse_error ? true : false
end
# Returns the error message for failed requests
def error
- @data['message'] unless success?
+ return if success? unless parse_error?
+ @data['error'] || @data['message']
end
# Return the URI for the request that generated this response
@@ -34,6 +45,16 @@ def body
@response.body
end
+ # Return the HTTP response code
+ def code
+ @response.code.to_i
+ end
+
+ # Return the raw Net:HTTPResponse object
+ def response
+ @response
+ end
+
# Support array notation
def []( key )
@data[ key ]
@@ -0,0 +1 @@
+{ "error": "My error is in the error property" }
@@ -0,0 +1 @@
+{ "error": "My error is in the message property" }
@@ -0,0 +1 @@
+This is a response that isn't JSON
View
@@ -90,5 +90,33 @@
end
end
+
+ context "error handling" do
+
+ it "can handle error errors" do
+ http_response = stub_http_response_with( 'error_response.json', 500 )
+ response = Zappos::Response.new( nil, nil, http_response )
+ response.error.should be_a_kind_of String
+ end
+
+ it "can handle message errors" do
+ http_response = stub_http_response_with( 'message_response.json', 500 )
+ response = Zappos::Response.new( nil, nil, http_response )
+ response.error.should be_a_kind_of String
+ end
+
+ it "can handle things that aren't json" do
+ http_response = stub_http_response_with( 'not_json.txt', 500 )
+ response = Zappos::Response.new( nil, nil, http_response )
+ response.error.should be_a_kind_of String
+ end
+
+ it "can handle things that aren't json and not normally errors" do
+ http_response = stub_http_response_with( 'not_json.txt', 200 )
+ response = Zappos::Response.new( nil, nil, http_response )
+ response.error.should be_a_kind_of String
+ end
+
+ end
end
View
@@ -14,10 +14,10 @@ def file_fixture(filename)
open(File.join(File.dirname(__FILE__), 'fixtures', "#{filename.to_s}")).read
end
-def stub_http_response_with(filename)
+def stub_http_response_with(filename,code=200)
format = filename.split('.').last.intern
data = file_fixture(filename)
- response = Net::HTTPOK.new( '1.1', 200, '' )
+ response = Net::HTTPResponse.new( '1.1', code, '' )
response.stub!(:body).and_return(data)
response
end
Oops, something went wrong.

0 comments on commit b1be3d7

Please sign in to comment.