blob: 85ff93a9fc50ba900cb5d436464568a6d7c68b70 [file] [log] [blame]
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001# Copyright 2010 Google Inc. All Rights Reserved.
2
Joe Gregorioaf276d22010-12-09 14:26:58 -05003"""Classes to encapsulate a single HTTP request.
Joe Gregorioc5c5a372010-09-22 11:42:32 -04004
Joe Gregorioaf276d22010-12-09 14:26:58 -05005The classes implement a command pattern, with every
6object supporting an execute() method that does the
7actuall HTTP request.
Joe Gregorioc5c5a372010-09-22 11:42:32 -04008"""
9
10__author__ = 'jcgregorio@google.com (Joe Gregorio)'
Joe Gregorioaf276d22010-12-09 14:26:58 -050011__all__ = [
12 'HttpRequest', 'RequestMockBuilder'
13 ]
14
Joe Gregorioc6722462010-12-20 14:29:28 -050015import httplib2
Joe Gregorio89174d22010-12-20 14:37:36 -050016from model import JsonModel
Joe Gregorioc5c5a372010-09-22 11:42:32 -040017
18
19class HttpRequest(object):
Joe Gregorioaf276d22010-12-09 14:26:58 -050020 """Encapsulates a single HTTP request.
Joe Gregorioc5c5a372010-09-22 11:42:32 -040021 """
22
Joe Gregorioabda96f2011-02-11 20:19:33 -050023 def __init__(self, http, postproc, uri, method="GET", body=None, headers=None,
24 methodId=None):
Joe Gregorioaf276d22010-12-09 14:26:58 -050025 """Constructor for an HttpRequest.
26
Joe Gregorioaf276d22010-12-09 14:26:58 -050027 Args:
28 http: httplib2.Http, the transport object to use to make a request
Joe Gregorioabda96f2011-02-11 20:19:33 -050029 postproc: callable, called on the HTTP response and content to transform
30 it into a data object before returning, or raising an exception
31 on an error.
Joe Gregorioaf276d22010-12-09 14:26:58 -050032 uri: string, the absolute URI to send the request to
33 method: string, the HTTP method to use
34 body: string, the request body of the HTTP request
35 headers: dict, the HTTP request headers
Joe Gregorioaf276d22010-12-09 14:26:58 -050036 methodId: string, a unique identifier for the API method being called.
37 """
Joe Gregorioc5c5a372010-09-22 11:42:32 -040038 self.uri = uri
39 self.method = method
40 self.body = body
41 self.headers = headers or {}
42 self.http = http
43 self.postproc = postproc
44
45 def execute(self, http=None):
46 """Execute the request.
47
Joe Gregorioaf276d22010-12-09 14:26:58 -050048 Args:
49 http: httplib2.Http, an http object to be used in place of the
50 one the HttpRequest request object was constructed with.
51
52 Returns:
53 A deserialized object model of the response body as determined
54 by the postproc.
55
56 Raises:
57 apiclient.errors.HttpError if the response was not a 2xx.
58 httplib2.Error if a transport error has occured.
Joe Gregorioc5c5a372010-09-22 11:42:32 -040059 """
60 if http is None:
61 http = self.http
62 resp, content = http.request(self.uri, self.method,
63 body=self.body,
64 headers=self.headers)
65 return self.postproc(resp, content)
Joe Gregorioaf276d22010-12-09 14:26:58 -050066
67
68class HttpRequestMock(object):
69 """Mock of HttpRequest.
70
71 Do not construct directly, instead use RequestMockBuilder.
72 """
73
74 def __init__(self, resp, content, postproc):
75 """Constructor for HttpRequestMock
76
77 Args:
78 resp: httplib2.Response, the response to emulate coming from the request
79 content: string, the response body
80 postproc: callable, the post processing function usually supplied by
81 the model class. See model.JsonModel.response() as an example.
82 """
83 self.resp = resp
84 self.content = content
85 self.postproc = postproc
86 if resp is None:
Joe Gregorioc6722462010-12-20 14:29:28 -050087 self.resp = httplib2.Response({'status': 200, 'reason': 'OK'})
Joe Gregorioaf276d22010-12-09 14:26:58 -050088 if 'reason' in self.resp:
89 self.resp.reason = self.resp['reason']
90
91 def execute(self, http=None):
92 """Execute the request.
93
94 Same behavior as HttpRequest.execute(), but the response is
95 mocked and not really from an HTTP request/response.
96 """
97 return self.postproc(self.resp, self.content)
98
99
100class RequestMockBuilder(object):
101 """A simple mock of HttpRequest
102
103 Pass in a dictionary to the constructor that maps request methodIds to
104 tuples of (httplib2.Response, content) that should be returned when that
105 method is called. None may also be passed in for the httplib2.Response, in
106 which case a 200 OK response will be generated.
107
108 Example:
109 response = '{"data": {"id": "tag:google.c...'
110 requestBuilder = RequestMockBuilder(
111 {
112 'chili.activities.get': (None, response),
113 }
114 )
115 apiclient.discovery.build("buzz", "v1", requestBuilder=requestBuilder)
116
117 Methods that you do not supply a response for will return a
118 200 OK with an empty string as the response content. The methodId
119 is taken from the rpcName in the discovery document.
120
121 For more details see the project wiki.
122 """
123
124 def __init__(self, responses):
125 """Constructor for RequestMockBuilder
126
127 The constructed object should be a callable object
128 that can replace the class HttpResponse.
129
130 responses - A dictionary that maps methodIds into tuples
131 of (httplib2.Response, content). The methodId
132 comes from the 'rpcName' field in the discovery
133 document.
134 """
135 self.responses = responses
136
Joe Gregorioabda96f2011-02-11 20:19:33 -0500137 def __call__(self, http, postproc, uri, method="GET", body=None,
138 headers=None, methodId=None):
Joe Gregorioaf276d22010-12-09 14:26:58 -0500139 """Implements the callable interface that discovery.build() expects
140 of requestBuilder, which is to build an object compatible with
141 HttpRequest.execute(). See that method for the description of the
142 parameters and the expected response.
143 """
144 if methodId in self.responses:
145 resp, content = self.responses[methodId]
146 return HttpRequestMock(resp, content, postproc)
147 else:
148 model = JsonModel()
149 return HttpRequestMock(None, '{}', model.response)