blob: 31a8fe8b3ef83e0dbd22d29afdcedb20ed20b76c [file] [log] [blame]
# Copyright 2010 Google Inc. All Rights Reserved.
"""Classes to encapsulate a single HTTP request.
The classes implement a command pattern, with every
object supporting an execute() method that does the
actuall HTTP request.
"""
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
__all__ = [
'HttpRequest', 'RequestMockBuilder'
]
import httplib2
from model import JsonModel
class HttpRequest(object):
"""Encapsulates a single HTTP request.
"""
def __init__(self, http, uri, method="GET", body=None, headers=None,
postproc=None, methodId=None):
"""Constructor for an HttpRequest.
Only http and uri are required.
Args:
http: httplib2.Http, the transport object to use to make a request
uri: string, the absolute URI to send the request to
method: string, the HTTP method to use
body: string, the request body of the HTTP request
headers: dict, the HTTP request headers
postproc: callable, called on the HTTP response and content to transform
it into a data object before returning, or raising an exception
on an error.
methodId: string, a unique identifier for the API method being called.
"""
self.uri = uri
self.method = method
self.body = body
self.headers = headers or {}
self.http = http
self.postproc = postproc
def execute(self, http=None):
"""Execute the request.
Args:
http: httplib2.Http, an http object to be used in place of the
one the HttpRequest request object was constructed with.
Returns:
A deserialized object model of the response body as determined
by the postproc.
Raises:
apiclient.errors.HttpError if the response was not a 2xx.
httplib2.Error if a transport error has occured.
"""
if http is None:
http = self.http
resp, content = http.request(self.uri, self.method,
body=self.body,
headers=self.headers)
return self.postproc(resp, content)
class HttpRequestMock(object):
"""Mock of HttpRequest.
Do not construct directly, instead use RequestMockBuilder.
"""
def __init__(self, resp, content, postproc):
"""Constructor for HttpRequestMock
Args:
resp: httplib2.Response, the response to emulate coming from the request
content: string, the response body
postproc: callable, the post processing function usually supplied by
the model class. See model.JsonModel.response() as an example.
"""
self.resp = resp
self.content = content
self.postproc = postproc
if resp is None:
self.resp = httplib2.Response({'status': 200, 'reason': 'OK'})
if 'reason' in self.resp:
self.resp.reason = self.resp['reason']
def execute(self, http=None):
"""Execute the request.
Same behavior as HttpRequest.execute(), but the response is
mocked and not really from an HTTP request/response.
"""
return self.postproc(self.resp, self.content)
class RequestMockBuilder(object):
"""A simple mock of HttpRequest
Pass in a dictionary to the constructor that maps request methodIds to
tuples of (httplib2.Response, content) that should be returned when that
method is called. None may also be passed in for the httplib2.Response, in
which case a 200 OK response will be generated.
Example:
response = '{"data": {"id": "tag:google.c...'
requestBuilder = RequestMockBuilder(
{
'chili.activities.get': (None, response),
}
)
apiclient.discovery.build("buzz", "v1", requestBuilder=requestBuilder)
Methods that you do not supply a response for will return a
200 OK with an empty string as the response content. The methodId
is taken from the rpcName in the discovery document.
For more details see the project wiki.
"""
def __init__(self, responses):
"""Constructor for RequestMockBuilder
The constructed object should be a callable object
that can replace the class HttpResponse.
responses - A dictionary that maps methodIds into tuples
of (httplib2.Response, content). The methodId
comes from the 'rpcName' field in the discovery
document.
"""
self.responses = responses
def __call__(self, http, uri, method="GET", body=None, headers=None,
postproc=None, methodId=None):
"""Implements the callable interface that discovery.build() expects
of requestBuilder, which is to build an object compatible with
HttpRequest.execute(). See that method for the description of the
parameters and the expected response.
"""
if methodId in self.responses:
resp, content = self.responses[methodId]
return HttpRequestMock(resp, content, postproc)
else:
model = JsonModel()
return HttpRequestMock(None, '{}', model.response)