Skip to content
This repository has been archived by the owner on May 12, 2020. It is now read-only.

Docstring comments #33

Merged
merged 7 commits into from Feb 5, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
89 changes: 56 additions & 33 deletions paypalrestsdk/api.py
Expand Up @@ -15,16 +15,18 @@

class Api:

# User-Agent for HTTP request
# User-Agent for HTTP request
library_details = "httplib2 %s; python %s" % (httplib2.__version__, platform.python_version())
user_agent = "PayPalSDK/rest-sdk-python %s (%s)" % (__version__, library_details)

# Create API object
# == Example
# import paypalrestsdk
# api = paypalrestsdk.Api( mode="sandbox",
# client_id='CLIENT_ID', client_secret='CLIENT_SECRET', ssl_options={} )

def __init__(self, options=None, **args):
"""Create API object

Usage::

>>> import paypalrestsdk
>>> api = paypalrestsdk.Api(mode="sandbox", client_id='CLIENT_ID', client_secret='CLIENT_SECRET', ssl_options={})
"""
args = util.merge_dict(options or {}, args)

self.mode = args.get("mode", "sandbox")
Expand All @@ -42,20 +44,22 @@ def __init__(self, options=None, **args):

self.options = args

# Default endpoint
def default_endpoint(self):
if self.mode == "live":
return "https://api.paypal.com"
else:
return "https://api.sandbox.paypal.com"

# Find basic auth
def basic_auth(self):
"""Find basic auth, and returns base64 encoded
"""
credentials = "%s:%s" % (self.client_id, self.client_secret)
return base64.b64encode(credentials.encode('utf-8')).decode('utf-8').replace("\n", "")

# Generate token_hash
def get_token_hash(self):
"""Generate new token by making a POST request with client credentials if
validate_token_hash finds token to be invalid
"""
self.validate_token_hash()
if self.token_hash is None:
self.token_request_at = datetime.datetime.now()
Expand All @@ -69,27 +73,33 @@ def get_token_hash(self):

return self.token_hash

# Validate expires_in
def validate_token_hash(self):
"""Checks if token duration has expired and if so resets token
"""
if self.token_request_at and self.token_hash and self.token_hash.get("expires_in") is not None:
delta = datetime.datetime.now() - self.token_request_at
duration = (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10**6) / 10**6
if duration > self.token_hash.get("expires_in"):
self.token_hash = None

# Get access token
def get_token(self):
return self.get_token_hash()["access_token"]

# Get token type
# Get token
def get_token_type(self):
"""Get token type e.g. Bearer
"""
return self.get_token_hash()["token_type"]

# Make HTTP call and Format Response
# == Example
# api.request("https://api.sandbox.paypal.com/v1/payments/payment?count=10", "GET", {})
# api.request("https://api.sandbox.paypal.com/v1/payments/payment", "POST", "{}", {} )
def request(self, url, method, body=None, headers=None):
"""Make HTTP call, formats response and does error handling. Uses http_call method in API class.

Usage::

>>> api.request("https://api.sandbox.paypal.com/v1/payments/payment?count=10", "GET", {})
>>> api.request("https://api.sandbox.paypal.com/v1/payments/payment", "POST", "{}", {} )

"""
http_headers = util.merge_dict(self.headers(), headers or {})

if http_headers.get('PayPal-Request-Id'):
Expand All @@ -110,8 +120,10 @@ def request(self, url, method, body=None, headers=None):
else:
raise error

# Make http Call
def http_call(self, url, method, **args):
"""
Makes a http call. Logs response information.
"""
logging.info('Request[%s]: %s' % (method, url))
http = httplib2.Http(**self.ssl_options)
start_time = datetime.datetime.now()
Expand All @@ -120,8 +132,9 @@ def http_call(self, url, method, **args):
logging.info('Response[%d]: %s, Duration: %s.%ss' % (response.status, response.reason, duration.seconds, duration.microseconds))
return self.handle_response(response, content.decode('utf-8'))

# Validate HTTP response
def handle_response(self, response, content):
"""Validate HTTP response
"""
status = response.status
if status in (301, 302, 303, 307):
raise Redirection(response, content)
Expand Down Expand Up @@ -150,38 +163,48 @@ def handle_response(self, response, content):
else:
raise ConnectionError(response, content, "Unknown response code: #{response.code}")

# Default HTTP headers
def headers(self):
"""Default HTTP headers
"""
return {
"Authorization": ("%s %s" % (self.get_token_type(), self.get_token())),
"Content-Type": "application/json",
"Accept": "application/json",
"User-Agent": self.user_agent
}

# Make GET request
# == Example
# api.get("v1/payments/payment?count=1")
# api.get("v1/payments/payment/PAY-1234")
def get(self, action, headers=None):
"""Make GET request

Usage::

>>> api.get("v1/payments/payment?count=1")
>>> api.get("v1/payments/payment/PAY-1234")
"""
return self.request(util.join_url(self.endpoint, action), 'GET', headers=headers or {})

# Make POST request
# == Example
# api.post("v1/payments/payment", { 'indent': 'sale' })
# api.post("v1/payments/payment/PAY-1234/execute", { 'payer_id': '1234' })
def post(self, action, params=None, headers=None):
"""Make POST request

Usage::

>>> api.post("v1/payments/payment", { 'indent': 'sale' })
>>> api.post("v1/payments/payment/PAY-1234/execute", { 'payer_id': '1234' })

"""
return self.request(util.join_url(self.endpoint, action), 'POST', body=json.dumps(params or {}), headers=headers or {})

# Make DELETE request
def delete(self, action, headers=None):
"""Make DELETE request
"""
return self.request(util.join_url(self.endpoint, action), 'DELETE', headers=headers or {})


__api__ = None

# Get default api
def default():
"""Returns default api object and if not present creates a new one
By default points to developer sandbox
"""
global __api__
if __api__ is None:
try:
Expand All @@ -194,11 +217,11 @@ def default():
return __api__


# Create new default api object with given configuration
def set_config(options=None, **config):
"""Create new default api object with given configuration
"""
global __api__
__api__ = Api(options or {}, **config)
return __api__

configure = set_config

56 changes: 29 additions & 27 deletions paypalrestsdk/exceptions.py
@@ -1,5 +1,4 @@


class ConnectionError(Exception):
def __init__(self, response, content=None, message=None):
self.response = response
Expand All @@ -15,86 +14,89 @@ def __str__(self):
return message


# Raised when a Timeout::Error occurs.
class TimeoutError(ConnectionError):
"""Raised when a Timeout::Error occurs.
"""
def __init__(self, message):
self.message = message

def __str__(self):
return self.message


# Raised when a OpenSSL::SSL::SSLError occurs.
class SSLError(ConnectionError):
"""Raised when a OpenSSL::SSL::SSLError occurs
"""
def __init__(self, message):
self.message = message

def __str__(self):
return self.message


# 3xx Redirection
class Redirection(ConnectionError):
"""3xx Redirection
"""
def __str__(self):
message = super(Redirection, self).__str__()
if self.response.get('Location'):
message = "%s => %s" % (message, self.response.get('Location'))
return message


class MissingParam(TypeError):
pass


class MissingConfig(Exception):
pass


# 4xx Client Error
class ClientError(ConnectionError):
"""4xx Client Error
"""
pass


# 400 Bad Request
class BadRequest(ClientError):
"""400 Bad Request
"""
pass


# 401 Unauthorized

class UnauthorizedAccess(ClientError):
"""401 Unauthorized
"""
pass


# 403 Forbidden
class ForbiddenAccess(ClientError):
"""403 Forbidden
"""
pass


# 404 Not Found
class ResourceNotFound(ClientError):
"""404 Not Found
"""
pass


# 409 Conflict

class ResourceConflict(ClientError):
"""409 Conflict
"""
pass


# 410 Gone
class ResourceGone(ClientError):
"""410 Gone
"""
pass

#422 Invalid
class ResourceInvalid(ClientError):
"""422 Invalid
"""
pass

# 5xx Server Error
class ServerError(ConnectionError):
"""5xx Server Error
"""
pass


# 405 Method Not Allowed

class MethodNotAllowed(ClientError):
"""405 Method Not Allowed
"""

def allowed_methods(self):
return self.response['Allow']