uber_rides/request.py (81 lines of code) (raw):
# Copyright (c) 2017 Uber Technologies, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Internal module for HTTP Requests and Responses."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from requests import Session
from string import ascii_letters
from string import digits
from uber_rides.errors import UberIllegalState
from uber_rides.utils import http
from uber_rides.utils.request import build_url
from uber_rides.utils.request import generate_data
from uber_rides.utils.request import generate_prepared_request
LIB_VERSION = '0.6.0'
class Response(object):
"""The response from an HTTP request."""
def __init__(self, response):
"""Initialize a Response.
Parameters
response (requests.Response)
The HTTP response from an API request.
"""
self.status_code = response.status_code
self.request = response.request
self.headers = response.headers
try:
self.json = response.json()
except:
self.json = None
class Request(object):
"""Request containing information to send to server."""
def __init__(
self,
auth_session,
api_host,
method,
path,
handlers=None,
args=None,
):
"""Initialize a Request.
Parameters
auth_session (Session)
Session object containing OAuth 2.0 credentials. Optional
for any HTTP Requests that don't need access headers.
api_host (str)
Base URL of the Uber Server that handles API calls.
method (str)
HTTP Method (e.g. 'POST').
path (str)
The endpoint path. (e.g. 'v1.2/products')
handlers (list[handler])
Optional list of error handlers to attach to the request.
args (dict)
Optional dictionary of arguments to add to the request.
"""
self.auth_session = auth_session
self.api_host = api_host
self.path = path
self.method = method
self.handlers = handlers or []
self.args = args
def _prepare(self):
"""Builds a URL and return a PreparedRequest.
Returns
(requests.PreparedRequest)
Raises
UberIllegalState (APIError)
"""
if self.method not in http.ALLOWED_METHODS:
raise UberIllegalState('Unsupported HTTP Method.')
api_host = self.api_host
headers = self._build_headers(self.method, self.auth_session)
url = build_url(api_host, self.path)
data, params = generate_data(self.method, self.args)
return generate_prepared_request(
self.method,
url,
headers,
data,
params,
self.handlers,
)
def _send(self, prepared_request):
"""Send a PreparedRequest to the server.
Parameters
prepared_request (requests.PreparedRequest)
Returns
(Response)
A Response object, whichcontains a server's
response to an HTTP request.
"""
session = Session()
response = session.send(prepared_request)
return Response(response)
def execute(self):
"""Prepare and send the Request, return a Response.
Returns
(Response)
The HTTP Response from an API Request
to the server.
Example
request = Request(session, 'api.uber.com', 'GET', 'v1.2/profile')
response = request.execute()
"""
prepared_request = self._prepare()
return self._send(prepared_request)
def _build_headers(self, method, auth_session):
"""Create headers for the request.
Parameters
method (str)
HTTP method (e.g. 'POST').
auth_session (Session)
The Session object containing OAuth 2.0 credentials.
Returns
headers (dict)
Dictionary of access headers to attach to request.
Raises
UberIllegalState (ApiError)
Raised if headers are invalid.
"""
token_type = auth_session.token_type
if auth_session.server_token:
token = auth_session.server_token
else:
token = auth_session.oauth2credential.access_token
if not self._authorization_headers_valid(token_type, token):
message = 'Invalid token_type or token.'
raise UberIllegalState(message)
headers = {
'Authorization': ' '.join([token_type, token]),
'X-Uber-User-Agent': 'Python Rides SDK v{}'.format(LIB_VERSION),
}
if method in http.BODY_METHODS:
headers.update(http.DEFAULT_CONTENT_HEADERS)
return headers
def _authorization_headers_valid(self, token_type, token):
"""Verify authorization headers for a request.
Parameters
token_type (str)
Type of token to access resources.
token (str)
Server Token or OAuth 2.0 Access Token.
Returns
(bool)
True iff token_type and token are valid.
"""
if token_type not in http.VALID_TOKEN_TYPES:
return False
allowed_chars = ascii_letters + digits + '.' + '_' + '-' + '='
# True if token only contains allowed_chars
return all(characters in allowed_chars for characters in token)