blob: 1f2e1c112e1489309666c71af9eec9baacae0eeb [file] [log] [blame]
Joe Gregorio3ad5e9a2010-12-09 15:01:04 -05001#!/usr/bin/python2.4
2#
3# Copyright 2010 Google Inc. All Rights Reserved.
4
5"""Model objects for requests and responses
6
7Each API may support one or more serializations, such
8as JSON, Atom, etc. The model classes are responsible
9for converting between the wire format and the Python
10object representation.
11"""
12
13__author__ = 'jcgregorio@google.com (Joe Gregorio)'
14
15import logging
16import urllib
17
Joe Gregoriob843fa22010-12-13 16:26:07 -050018from anyjson import simplejson
19from errors import HttpError
Joe Gregorio3ad5e9a2010-12-09 15:01:04 -050020
Joe Gregorioabda96f2011-02-11 20:19:33 -050021def _abstract():
22 raise NotImplementedError('You need to override this function')
Joe Gregorio3ad5e9a2010-12-09 15:01:04 -050023
Joe Gregorioabda96f2011-02-11 20:19:33 -050024
25class Model(object):
26 """Model base class.
27
28 All Model classes should implement this interface.
29 The Model serializes and de-serializes between a wire
30 format such as JSON and a Python object representation.
31 """
32
33 def request(self, headers, path_params, query_params, body_value):
34 """Updates outgoing requests with a deserialized body.
35
36 Args:
37 headers: dict, request headers
38 path_params: dict, parameters that appear in the request path
39 query_params: dict, parameters that appear in the query
40 body_value: object, the request body as a Python object, which must be
41 serializable.
42 Returns:
43 A tuple of (headers, path_params, query, body)
44
45 headers: dict, request headers
46 path_params: dict, parameters that appear in the request path
47 query: string, query part of the request URI
48 body: string, the body serialized in the desired wire format.
49 """
50 _abstract()
51
52 def response(self, resp, content):
53 """Convert the response wire format into a Python object.
54
55 Args:
56 resp: httplib2.Response, the HTTP response headers and status
57 content: string, the body of the HTTP response
58
59 Returns:
60 The body de-serialized as a Python object.
61
62 Raises:
63 apiclient.errors.HttpError if a non 2xx response is received.
64 """
65 _abstract()
66
67
68class JsonModel(Model):
Joe Gregorio3ad5e9a2010-12-09 15:01:04 -050069 """Model class for JSON.
70
71 Serializes and de-serializes between JSON and the Python
72 object representation of HTTP request and response bodies.
73 """
74
75 def request(self, headers, path_params, query_params, body_value):
76 """Updates outgoing requests with JSON bodies.
77
78 Args:
79 headers: dict, request headers
80 path_params: dict, parameters that appear in the request path
81 query_params: dict, parameters that appear in the query
82 body_value: object, the request body as a Python object, which must be
83 serializable by simplejson.
84 Returns:
85 A tuple of (headers, path_params, query, body)
86
87 headers: dict, request headers
88 path_params: dict, parameters that appear in the request path
89 query: string, query part of the request URI
90 body: string, the body serialized as JSON
91 """
92 query = self._build_query(query_params)
93 headers['accept'] = 'application/json'
94 if 'user-agent' in headers:
95 headers['user-agent'] += ' '
96 else:
97 headers['user-agent'] = ''
98 headers['user-agent'] += 'google-api-python-client/1.0'
99 if body_value is None:
100 return (headers, path_params, query, None)
101 else:
102 headers['content-type'] = 'application/json'
103 return (headers, path_params, query, simplejson.dumps(body_value))
104
105 def _build_query(self, params):
106 """Builds a query string.
107
108 Args:
109 params: dict, the query parameters
110
111 Returns:
112 The query parameters properly encoded into an HTTP URI query string.
113 """
114 params.update({'alt': 'json'})
115 astuples = []
116 for key, value in params.iteritems():
117 if getattr(value, 'encode', False) and callable(value.encode):
118 value = value.encode('utf-8')
119 astuples.append((key, value))
120 return '?' + urllib.urlencode(astuples)
121
122 def response(self, resp, content):
123 """Convert the response wire format into a Python object.
124
125 Args:
126 resp: httplib2.Response, the HTTP response headers and status
127 content: string, the body of the HTTP response
128
129 Returns:
130 The body de-serialized as a Python object.
131
132 Raises:
133 apiclient.errors.HttpError if a non 2xx response is received.
134 """
135 # Error handling is TBD, for example, do we retry
136 # for some operation/error combinations?
137 if resp.status < 300:
138 if resp.status == 204:
139 # A 204: No Content response should be treated differently
140 # to all the other success states
141 return simplejson.loads('{}')
142 body = simplejson.loads(content)
143 if isinstance(body, dict) and 'data' in body:
144 body = body['data']
145 return body
146 else:
147 logging.debug('Content from bad request was: %s' % content)
Ali Afshar2dcc6522010-12-16 10:11:53 +0100148 raise HttpError(resp, content)