| # 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' |
| ] |
| |
| from httplib2 import Response |
| from apiclient.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 = 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) |