1from __future__ import unicode_literals
2
3import logging
4
5from rbtools.api.decode import decode_response
6from rbtools.api.factory import create_resource
7from rbtools.api.request import HttpRequest, ReviewBoardServer
8from rbtools.api.transport import Transport
9
10
11class SyncTransport(Transport):
12    """A synchronous transport layer for the API client.
13
14    The file provided in cookie_file is used to store and retrieve
15    the authentication cookies for the API.
16
17    The optional agent parameter can be used to specify a custom
18    User-Agent string for the API. If not provided, the default
19    RBTools User-Agent will be used.
20
21    The optional session can be used to specify an 'rbsessionid'
22    to use when authenticating with reviewboard.
23    """
24    def __init__(self, url, cookie_file=None, username=None, password=None,
25                 api_token=None, agent=None, session=None, disable_proxy=False,
26                 auth_callback=None, otp_token_callback=None,
27                 verify_ssl=True, allow_caching=True,
28                 cache_location=None, in_memory_cache=False,
29                 save_cookies=True, ext_auth_cookies=None,
30                 ca_certs=None, client_key=None, client_cert=None,
31                 *args, **kwargs):
32        super(SyncTransport, self).__init__(url, *args, **kwargs)
33        self.allow_caching = allow_caching
34        self.cache_location = cache_location
35        self.in_memory_cache = in_memory_cache
36        self.server = ReviewBoardServer(
37            self.url,
38            cookie_file=cookie_file,
39            username=username,
40            password=password,
41            api_token=api_token,
42            session=session,
43            disable_proxy=disable_proxy,
44            auth_callback=auth_callback,
45            otp_token_callback=otp_token_callback,
46            verify_ssl=verify_ssl,
47            save_cookies=save_cookies,
48            ext_auth_cookies=ext_auth_cookies,
49            ca_certs=ca_certs,
50            client_key=client_key,
51            client_cert=client_cert)
52
53    def get_root(self):
54        return self._execute_request(HttpRequest(self.server.url))
55
56    def get_path(self, path, *args, **kwargs):
57        if not path.endswith('/'):
58            path = path + '/'
59
60        if path.startswith('/'):
61            path = path[1:]
62
63        return self._execute_request(
64            HttpRequest(self.server.url + path, query_args=kwargs))
65
66    def get_url(self, url, *args, **kwargs):
67        if not url.endswith('/'):
68            url = url + '/'
69
70        return self._execute_request(HttpRequest(url, query_args=kwargs))
71
72    def login(self, username, password):
73        self.server.login(username, password)
74
75    def logout(self):
76        self.server.logout()
77
78    def execute_request_method(self, method, *args, **kwargs):
79        request = method(*args, **kwargs)
80
81        if isinstance(request, HttpRequest):
82            return self._execute_request(request)
83
84        return request
85
86    def _execute_request(self, request):
87        """Execute an HTTPRequest and construct a resource from the payload"""
88        logging.debug('Making HTTP %s request to %s',
89                      request.method, request.url)
90
91        rsp = self.server.make_request(request)
92        info = rsp.info()
93        mime_type = info['Content-Type']
94        item_content_type = info.get('Item-Content-Type', None)
95
96        if request.method == 'DELETE':
97            # DELETE calls don't return any data. Everything else should.
98            return None
99        else:
100            payload = rsp.read()
101            payload = decode_response(payload, mime_type)
102
103            return create_resource(self, payload, request.url,
104                                   mime_type=mime_type,
105                                   item_mime_type=item_content_type)
106
107    def enable_cache(self):
108        """Enable caching for all future HTTP requests.
109
110        The cache will be created at the default location if none is provided.
111
112        If the in_memory parameter is True, the cache will be created in memory
113        instead of on disk. This overrides the cache_location parameter.
114        """
115        if self.allow_caching:
116            self.server.enable_cache(cache_location=self.cache_location,
117                                     in_memory=self.in_memory_cache)
118
119    def __repr__(self):
120        return '<%s(url=%r, cookie_file=%r, agent=%r)>' % (
121            self.__class__.__name__,
122            self.url,
123            self.server.cookie_file,
124            self.server.agent)
125