Python library for the Kloudless API

Python library for the Kloudless API

Python library for the Kloudless API.

You need to sign up and create an application first before using this SDK.

Requirements

Python 2.7 or Python 3.5+

Installation

Install via pip:

pip install kloudless

Install from source:

git clone git://github.com/kloudless/kloudless-python
cd kloudless-python
python setup.py install

Getting Started

Most Kloudless API endpoints require connecting to an upstream service account first. Start by navigating to API Explorer and connecting an account.

Get the Bearer Token

Get the Bearer Token

After the account has been connected, copy the Bearer Token from the text box and use it to initialize an Account object:

from kloudless import Account
account = Account(token="YOUR_BEARER_TOKEN")

Full Documentation

Full documentation is hosted atRead the docs. A quick-start is included below.

Making API Requests

You can now make an API request with the account instance you’ve created.

If Connecting to a Storage Service

# retrieve folder contents
root_folder_contents = account.get('storage/folders/root/contents')
for resource in root_folder_contents.get_paging_iterator():
    print(resource.data)

# download the first file in root_folder
for resource in root_folder_contents:
    if resource.data['type'] == 'file':
        filename = resource.data['name']
        response = resource.get('contents')
        with open(filename, 'wb') as f:
            f.write(response.content)
        break

# upload a file to root_folder
file_name = 'FILE_NAME_TO_UPLOAD'
headers = {
    'X-Kloudless-Metadata': json.dumps(
        {'parent_id': 'root', 'name': file_name}
    )
}
with open(file_name, 'rb') as f:
    file_resource = account.post('storage/files', data=f, headers=headers)

If Connecting to a Calendar Service

# retrieve primary calendar
calendar = account.get('cal/calendars/primary')
print('Primary Calendar: {}'.format(calendar.data['name']))

# iterate through events in first page with page_size equals 5
events = calendar.get('events?page_size=5')
for e in events:
    data = e.data
    print('{}: {}~{}'.format(data['name'], data['start'], data['end']))

# iterate thorough events in second page
next_page_events = events.get_next_page()
for e in next_page_events:
    data = e.data
    print('{}: {}~{}'.format(data['name'], data['start'], data['end']))

# create a new event on primary calendar
event = events.post(json={
    'start': '2019-01-01T12:30:00Z',
    'end': '2019-01-01T13:30:00Z',
    'name': 'Event test'}
)

Integrating OAuth Flow

You can use the Authenticator JS library to authenticate end-users via a pop-up and store the token server-side. Be sure to verify the token once it is transferred to your server. See kloudless.application.verify_token.

An alternate approach is to use the OAuth Authorization Code grant flow to redirect the end-user to Kloudless to connect their account to your app.

OAuth Integration Demo server

examples/demo_server.py provides the server-side logic of the 3-legged OAuth flow using helper methods from the Kloudless Python SDK. See examples/README.md for instructions on running the demo server.

Python Django Sample code

Insert the following code into Django views under views/ directory and calling it via urls.py.

from django.http import HttpResponseRedirect, HttpResponse
from django.conf import settings

from kloudless import get_authorization_url, get_token_from_code


def start_authorization_flow(request):
    """
    Redirect the user to start authorization flow.
    """
    url, state = get_authorization_url(app_id=settings.KLOUDLESS_APP_ID,
                                       redirect_uri=settings.KLOUDLESS_REDIRECT_URL,
                                       scope='storage')

    request.session['authorization_state'] = state
    return HttpResponseRedirect(url)


def callback(request):
    """
    The endpoint for settings.KLOUDLESS_REDIRECT_URL.
    """
    params = request.GET.dict()
    token = get_token_from_code(app_id=settings.KLOUDLESS_APP_ID,
                                api_key=settings.KLOUDLESS_API_KEY,
                                orig_state=request.session['authorization_state'],
                                orig_redirect_uri=settings.KLOUDLESS_REDIRECT_URL,
                                **params)

    # store the token
    request.user.kloudless_token = token
    request.user.save()
    return HttpResponse('Account connects successfully.')

Core Concepts

Logging Request Urls during Development

You may find it useful to enable logging for every http request by setting the log level to DEBUG. It will print the exact http url being requested.

from kloudless.util import logger

logger.setLevel('DEBUG')

Verifying the Bearer token

If the bearer token was transferred to the server from Authenticator JS library or the client-side implicit grant flow, it’s highly recommended that you verify the token.

from kloudless import verify_token
from kloudless import exceptions

app_id = "YOUR_APP_ID"
token = "BEARER_TOKEN_TO_VERIFY"
try:
    token_info = verify_token(app_id, token)
except exceptions.TokenVerificationFailed:
    print("Token:{} does not belong to App:{}".format(token, app_id))
except exceptions.APIException as e:
    # if other API request errors happened
    print(e.message)
else:
    print(token_info)

Modifying Global Config

  • base_url: default to https://api.kloudless.com
  • api_version: default to 1
from kloudless import configuration
from kloudless import Account

account = Account(token="YOUR_BEARER_TOKEN")
print(account.url) # 'https://api.kloudless.com/v1/accounts/me'

# Configuration changes would affect the Account and Client create afterward
configuration['base_url'] = 'http://localhost:8002'
configuration['api_version'] = '2'

account = Account(token="YOUR_BEARER_TOKEN")
print(account.url) # 'http://localhost:8002/v2/accounts/me'

Modifying API Version for One Request

You don’t have to modify the global config if you only want to change the api version for particular endpoints. The api_version parameter is available for all http methods.

For more options available for http methods, see kloudless.client.Session.request().

from kloudless import Account

account = Account(token="YOUR_BEARER_TOKEN")

# Request to 'https://api.kloudless.com/v1/accounts/me'
response = account.get()

# Request to 'https://api.kloudless.com/v2/accounts/me'
response = account.get(api_version=2)

Getting Upstream Raw Object

If you want to access the raw object from the upstream service, please set the get_raw_data parameter to True. This is available for all http methods.

For more options available for http methods, see kloudless.client.Session.request().

from kloudless import Account

account = Account(token="BEARER_TOKEN_FOR_CALENDAR_SERVICE")

calendar = account.get('cal/calendars/primary', get_raw_data=True)
print(calendar.data['raw'])

Setting Default Headers for All Requests

Setting default headers for all requests is useful whenever you want to always include raw data or make use of user-impersonation.

from kloudless import Account

account = Account(token='BEARER_TOKEN_FOR_CALENDAR_SERVICE')
account.headers['X-Kloudless-Raw-Data'] = 'true'

# the request afterward would include raw data by default
calendars = account.get('cal/calendars')

Retrieving Events

Once you enable activity monitoring for your application, you’ll be able to query for activity using our Events API. See Events Documentation for more information.

from kloudless import Account

account = Account(token="YOUR_BEARER_TOKEN")

# You can skip this step by using the cursor you stored.
cursor = account.get('events/latest').data['cursor']

# Iterating all events through pages with page_size equals 50
events = account.get('events', params={'cursor': cursor, 'page_size': 50})
for event in events.get_paging_iterator():
    print(event.data)

# You can store the latest cursor for next time usage
latest_cursor = events.latest_cursor

Calling Upstream Service APIs

You can make requests to the upstream service API (a.k.a. pass-through requests) by using kloudless.account.Account.raw().

from kloudless import Account

account = Account(token="'BEARER_TOKEN_FOR_GDRIVE_SERVICE'")

# Using raw method to forward request to Google Drive API
response = account.raw('GET', '/drive/v2/about')

Making Application Level Requests

In rare cases, you may want to make requests in the application level. You can make use of kloudless.client.Client for requests.

from kloudless import Client

# See https://developers.kloudless.com/docs/latest/meta#introduction-to-the-meta-api
# Use meta bearer token to initialize Client
meta_client = Client(token='YOUR_META_BEARER_TOKEN')

# Get info of your applications
applications = meta_client.get('meta/applications')
print(applications.data)

# Use API key to initialize Client
client = Client(api_key='YOUR_API_KEY')

# Get all accounts connect to your application
accounts = client.get('accounts')
print(accounts.data)

kloudless.application - Oauth methods

kloudless.application.verify_token(app_id, token)

Verify whether the token belongs to Application with app_id.

Refer to Verify the token for more information.

Parameters:
  • app_id (str) – Application ID
  • token (str) – Account’s Bearer token
Returns:

(dict) Token information

Raise:

kloudless.exceptions.TokenVerificationFailed

kloudless.application.get_authorization_url(app_id, redirect_uri, scope='all', state='', extra_data='', **params)

Get the url to start the first leg of OAuth flow.

Refer to Authentication Docs for more information.

Parameters:
  • app_id (str) – Application ID
  • redirect_uri (str) – Redirect URI to your application server
  • scope (str) – A space-delimited string of scopes that indicate which services a user can connect, and which permissions to request
  • state (str) – An arbitrary string which would be redirected back via redirect_uri as query parameter. Random url-safe Base64 string would be generated by default
  • extra_data (str) – A URL-encoded JSON object containing data used to pre-fill default values for fields in the Kloudless authentication forms. For example, the domain of a WebDAV server
  • params – Additional query parameters
Returns:

tuple(url, state): Redirect the user to url to start authorization. Saved state in user’s session for future validation

Return type:

tuple(str, str)

kloudless.application.get_token_from_code(app_id, api_key, orig_state, orig_redirect_uri, **params)

Retrieve bearer token from authorization code.

Parameters:
Returns:

(str) Bearer token

Raise:

kloudless.exceptions.OauthFlowFailed

kloudless.client - Client

class kloudless.client.Session

Bases: requests.sessions.Session

The Session class helps build Kloudless specific headers.

__init__()

Initialize self. See help(type(self)) for accurate signature.

request(method, url, api_version=None, get_raw_data=None, raw_headers=None, impersonate_user_id=None, **kwargs)

Override requests.Session.request() with additional parameters.

See API-wide options for more information about get_raw_data, raw_headers and impersonate_user_id parameters.

Parameters:
  • method (str) – Http method
  • url (str) – Request url
  • api_version (int) – API version
  • get_raw_data (bool) – Set to True if the raw object from upstream service is present. This is equal to the X-Kloudless-Raw-Data request header.
  • raw_headers (dict) – Headers fowarded to upstream service. This is equal to the X-Kloudless-Raw-Headers request header
  • impersonate_user_id (str) – User id to access or modify data for individual user accounts. This is equal to the X-Kloudless-As-User request header.
  • kwargs – kwargs passed to requests.Session.request()
Returns:

requests.Response

Raises:

kloudless.exceptions.APIException or its subclasses

class kloudless.client.Client(api_key=None, token=None)

Bases: kloudless.client.Session

Base Client class to send all http requests in this library.

Instance attributes

Variables:url (str) – Base url that will be used as a prefix for all http method calls
__init__(api_key=None, token=None)

Either api_key or token is needed for instantiation.

Parameters:
  • api_key – API key
  • token – Bearer token
request(method, path='', get_raw_response=False, **kwargs)
Note that the actual request url will have self.url as a prefix.
Parameters:
  • method (str) – Http method
  • path (str) – Request path
  • get_raw_response (str) – Set to True if the raw requests.Response instance is in the returned value
  • kwargs – kwargs passed to kloudless.client.Session.request()
Returns:

get(path='', **kwargs)
Http GET request.
Note that the actual request url will have self.url as a prefix.
Parameters:
Returns:

kloudless.resources.base.Response or its subclass

post(path='', data=None, json=None, **kwargs)
Http POST request.
Note that the actual request url will have self.url as a prefix.
Parameters:
  • path (str) – Request path
  • data – passed to request.Request.post()
  • json – passed to request.Request.post()
  • kwargs – See kloudless.client.Client.request() for more options.
Returns:

kloudless.resources.base.Response or its subclass

put(path='', data=None, **kwargs)
Http PUT request.
Note that the actual request url will have self.url as a prefix.
Parameters:
Returns:

kloudless.resources.base.Response or its subclass

patch(path='', data=None, **kwargs)
Http PATCH request.
Note that the actual request url will have self.url as a prefix.
Parameters:
Returns:

kloudless.resources.base.Response or its subclass

delete(path='', **kwargs)
Http DELETE request.
Note that the actual request url will have self.url as a prefix.
Parameters:
Returns:

kloudless.resources.base.Response or its subclass

kloudless.account - Account

class kloudless.account.Account(token=None, api_key=None, account_id=None)

Bases: kloudless.client.Client

Account class that represents one Kloudless account.

Instance attributes

Variables:url (str) – Base url which would be used as prefix for all http method calls
__init__(token=None, api_key=None, account_id=None)

Either token or api_key is needed for instantiation. account_id is needed if api_key is specified.

Parameters:
  • token – Bearer token
  • api_key – API key
  • account_id – Account ID
raw(raw_method, raw_uri, **kwargs)

Method for Pass-Through API

Parameters:
  • raw_method (str) – The value stand for X-Kloudless-Raw-Method header
  • raw_uri – The value stand for X-Kloudless-Raw-URI header
  • kwargs – kwargs passed to kloudless.client.Client.post()
Returns:

requests.Response

kloudless.account.get_verified_account(app_id, token)

Verify the token belongs to an Application with app_id and return an kloudless.account.Account instance.

Parameters:
  • app_id (str) – Application ID
  • token (str) – Account’s Bearer token
Returns:

kloudless.account.Account

Raise:

kloudless.exceptions.TokenVerificationFailed

kloudless.resources.base - Resource

class kloudless.resources.base.Response(client, url, response=None)

Bases: object

Base Response class for this library.

Instance attributes

Variables:
get(path='', **kwargs)
Performs http GET request through self.client.get.
Note that the actually request url would have self.url as prefix.
Returns:kloudless.resources.base.Response or its subclass
post(path='', data=None, json=None, **kwargs)
Performs http POST request through self.client.post.
Note that the actually request url would have self.url as prefix.
Returns:kloudless.resources.base.Response or its subclass
put(path='', data=None, **kwargs)
Performs http PUT request through self.client.put.
Note that the actually request url would have self.url as prefix.
Returns:kloudless.resources.base.Response or its subclass
patch(path='', data=None, **kwargs)
Performs http PATCH request through self.client.patch.
Note that the actually request url would have self.url as prefix.
Returns:kloudless.resources.base.Response or its subclass
delete(path='', **kwargs)
Performs http DELETE request through self.client.delete.
Note that the actually request url would have self.url as prefix.
Returns:kloudless.resources.base.Response or its subclass
refresh()

Performs GET request through self.client.get to self.url, then refresh self. The original query parameters and headers would be reused if original request is http GET request.

class kloudless.resources.base.ResponseJson(data, **kwargs)

Bases: kloudless.resources.base.Response

Base Response class for JSON response.

Instance attributes

Variables:data (dict) – JSON data
refresh()

Perform GET request through self.client.get to self.url, then refresh self. The original query parameters and headers would be reused if original request is http GET request.

class kloudless.resources.base.Resource(**kwargs)

Bases: kloudless.resources.base.ResponseJson

Represents a resource object from API response. A resource object contains an identifier and endpoint ( /{resource_type}/{identifier} ) for retrieving its metadata.

Example resources include: Files and folders in the Storage API, calendar events in the Calendar API, and events in Events API.

class kloudless.resources.base.ResourceList(**kwargs)

Bases: kloudless.resources.base.ResponseJson

Represents a list of resources from API response. ResourceList itself is also an iterable thorough self.objects.

Instance attributes

Variables:objects – list of kloudless.resource.base.Resource instance
get_next_page()

Get the resources of the next page, if any.

Returns:kloudless.resources.base.ResourceList
Raise:kloudless.exceptions.NoNextPage
get_paging_iterator(max_resources=None)

Generator to iterate thorough all resources under self.objects and all resources in the following page, if any.

If retrieving events, self.latest_cursor is available after iterating thorough all events without max_resources specified.

Parameters:max_resources – the maximum quantity of resources that would be contained in the returned generator
Returns:generator that yield kloudless.resources.base.Resource instance

kloudless.exceptions - Exceptions

exception kloudless.exceptions.KloudlessException(message='')

Bases: Exception

Base exception class inherited by all exceptions of the library.

Instance attributes

Variables:message (str) – Error message
default_message
exception kloudless.exceptions.InvalidParameter(message='')

Bases: kloudless.exceptions.KloudlessException

The parameters are invalid in a function call or class instantiation.

default_message = 'The parameter is not valid.'
exception kloudless.exceptions.TokenVerificationFailed(message='')

Bases: kloudless.exceptions.KloudlessException

default_message = 'The token does not belong to your application.'
exception kloudless.exceptions.OauthFlowFailed(message='')

Bases: kloudless.exceptions.KloudlessException

default_message = 'Oauth authorization flow failed.'
exception kloudless.exceptions.NoNextPage(cursor=None, *args, **kwargs)

Bases: kloudless.exceptions.KloudlessException

default_message = "There's no next page."
exception kloudless.exceptions.APIException(response, message='')

Bases: kloudless.exceptions.KloudlessException

Base Exception class for API requests.

Instance attributes

Variables:
  • responserequests.Response instance if available
  • status (int) – response.status_code
  • error_data (dict) – response.json()
default_message = 'Request failed.'
exception kloudless.exceptions.AuthorizationException(response, message='')

Bases: kloudless.exceptions.APIException

Exception class for 401 status code.

default_message = 'Authorization failed. Please double check that the API Key or Token being used is correct.'
exception kloudless.exceptions.ForbiddenException(response, message='')

Bases: kloudless.exceptions.APIException

Exception class for 403 status code.

default_message = 'Request forbidden. The action is not allowed.'
exception kloudless.exceptions.NotFoundException(response, message='')

Bases: kloudless.exceptions.APIException

Exception class for 404 status code.

default_message = 'Not found. Please make sure the url is correct.'
exception kloudless.exceptions.RateLimitException(*args, **kwargs)

Bases: kloudless.exceptions.APIException

Exception class for 429 status code.

Instance attributes

Variables:retry_after (float) – Delay seconds until next available requests
default_message = 'Rate limiting encountered. Please try again later.'
exception kloudless.exceptions.ServerException(response, message='')

Bases: kloudless.exceptions.APIException

Exception class for 5xx status code.

default_message = 'An unknown error occurred! Please contact support@kloudless.com with the Request ID for more details.'