blob: fb2bdf8998193bc68fecee0a3a1c55a4c3ec22e3 [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
18from apiclient.json import simplejson
19from apiclient.errors import HttpError
20
21
22class JsonModel(object):
23 """Model class for JSON.
24
25 Serializes and de-serializes between JSON and the Python
26 object representation of HTTP request and response bodies.
27 """
28
29 def request(self, headers, path_params, query_params, body_value):
30 """Updates outgoing requests with JSON bodies.
31
32 Args:
33 headers: dict, request headers
34 path_params: dict, parameters that appear in the request path
35 query_params: dict, parameters that appear in the query
36 body_value: object, the request body as a Python object, which must be
37 serializable by simplejson.
38 Returns:
39 A tuple of (headers, path_params, query, body)
40
41 headers: dict, request headers
42 path_params: dict, parameters that appear in the request path
43 query: string, query part of the request URI
44 body: string, the body serialized as JSON
45 """
46 query = self._build_query(query_params)
47 headers['accept'] = 'application/json'
48 if 'user-agent' in headers:
49 headers['user-agent'] += ' '
50 else:
51 headers['user-agent'] = ''
52 headers['user-agent'] += 'google-api-python-client/1.0'
53 if body_value is None:
54 return (headers, path_params, query, None)
55 else:
56 headers['content-type'] = 'application/json'
57 return (headers, path_params, query, simplejson.dumps(body_value))
58
59 def _build_query(self, params):
60 """Builds a query string.
61
62 Args:
63 params: dict, the query parameters
64
65 Returns:
66 The query parameters properly encoded into an HTTP URI query string.
67 """
68 params.update({'alt': 'json'})
69 astuples = []
70 for key, value in params.iteritems():
71 if getattr(value, 'encode', False) and callable(value.encode):
72 value = value.encode('utf-8')
73 astuples.append((key, value))
74 return '?' + urllib.urlencode(astuples)
75
76 def response(self, resp, content):
77 """Convert the response wire format into a Python object.
78
79 Args:
80 resp: httplib2.Response, the HTTP response headers and status
81 content: string, the body of the HTTP response
82
83 Returns:
84 The body de-serialized as a Python object.
85
86 Raises:
87 apiclient.errors.HttpError if a non 2xx response is received.
88 """
89 # Error handling is TBD, for example, do we retry
90 # for some operation/error combinations?
91 if resp.status < 300:
92 if resp.status == 204:
93 # A 204: No Content response should be treated differently
94 # to all the other success states
95 return simplejson.loads('{}')
96 body = simplejson.loads(content)
97 if isinstance(body, dict) and 'data' in body:
98 body = body['data']
99 return body
100 else:
101 logging.debug('Content from bad request was: %s' % content)
102 if resp.get('content-type', '').startswith('application/json'):
103 raise HttpError(resp, simplejson.loads(content)['error'])
104 else:
105 raise HttpError(resp, '%d %s' % (resp.status, resp.reason))