Added mocks for the generated service objects. Also fixed a bunch of formatting.
diff --git a/apiclient/http.py b/apiclient/http.py
index 25d646e..d616c07 100644
--- a/apiclient/http.py
+++ b/apiclient/http.py
@@ -1,19 +1,42 @@
# Copyright 2010 Google Inc. All Rights Reserved.
-"""One-line documentation for http module.
+"""Classes to encapsulate a single HTTP request.
-A detailed description of http.
+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'
+ ]
+
+from httplib2 import Response
+from apiclient.model import JsonModel
class HttpRequest(object):
- """Encapsulate an HTTP request.
+ """Encapsulates a single HTTP request.
"""
def __init__(self, http, uri, method="GET", body=None, headers=None,
- postproc=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
@@ -24,8 +47,17 @@
def execute(self, http=None):
"""Execute the request.
- If an http object is passed in it is used instead of the
- httplib2.Http object that the request was constructed with.
+ 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
@@ -33,3 +65,87 @@
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 = 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)