Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0e59190
Configure tests for setup.py
Ogaday Aug 18, 2017
6b10ef9
Create tests for Workspaces endpoint
Ogaday Aug 18, 2017
7f906d2
Implement endpoint & workspaces endpoint
Ogaday Aug 18, 2017
dbef0b2
Update README with testing instructions
Ogaday Aug 18, 2017
92e2407
Update workspace endpoints to be more compatible with everything
Ogaday Aug 18, 2017
d70dd5f
Hopefully make travis pass.
Ogaday Aug 18, 2017
1c39632
Merge remote-tracking branch 'upstream/dev' into dev
Ogaday Aug 19, 2017
3434c47
Update test docstrings
Ogaday Aug 22, 2017
b0e5f64
Fix bug caused by mutable keyword arguments
Ogaday Aug 22, 2017
70e8f79
Merge pull request #1 from Ogaday/bug-fix-1
Ogaday Aug 22, 2017
68c787f
Refactor request calling
Ogaday Aug 22, 2017
b730966
Merge branch 'dev' of github.com:Ogaday/sapi-python-client into dev
Ogaday Aug 22, 2017
471acb8
Make put public
Ogaday Aug 22, 2017
3770230
Add docstring to put method
Ogaday Aug 22, 2017
beb2b88
Rename url related variables
Ogaday Aug 22, 2017
ab12358
Remove unnecessary extend function
Ogaday Aug 22, 2017
1e37280
Tidy up for flake8
Ogaday Aug 22, 2017
f9a9907
Improve docstrings
Ogaday Aug 22, 2017
ac03713
Create jobs endpoint
Ogaday Aug 22, 2017
1cf783d
Extend jobs endpoint
Ogaday Aug 22, 2017
038f8ac
Modify module docstrings
Ogaday Aug 22, 2017
32e7486
Make flake8 pass
Ogaday Aug 22, 2017
1d2d696
Start refactor of Client class
Ogaday Aug 22, 2017
4c44d80
Remove print statements
Ogaday Aug 22, 2017
0fb7ef5
Remove errant line
Ogaday Aug 23, 2017
5e75bf7
Add tests for the base endpoint HTTP methods
Ogaday Aug 23, 2017
fb9186b
Fix squashed `raise` gotcha
Ogaday Aug 23, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ FROM python:3.6

WORKDIR /code
COPY . /code/
RUN pip3 install --no-cache-dir flake8
RUN pip3 install --no-cache-dir flake8 responses
RUN python setup.py install
ENTRYPOINT ["python"]
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ Docker image with pre-installed library is also available, run it via:
docker run -i -t quay.io/keboola/sapi-python-client
```

## Tests

```bash
$ git clone https://github.com/keboola/sapi-python-client.git && cd sapi-python-client
$ python setup.py test
```


Under development -- all contributions very welcome :)

Kickstarted via https://gist.github.com/Halama/6006960
128 changes: 128 additions & 0 deletions kbcstorage/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""
Base classes for constructing the client.

Primarily exposes a base Endpoint class which deduplicates functionality across
various endpoints, such as tables, workspaces, jobs, etc. as described in the
`Storage API documentation`.


.. _Storage API documentation:
http://docs.keboola.apiary.io/
"""
import requests


class Endpoint:
"""
Base class for implementing a single endpoint related to a single entities
as described in the Storage API.

Attributes:
base_url (str): The base URL for this endpoint.
token (str): A key for the Storage API.
"""
def __init__(self, root_url, path_component, token):
"""
Create an endpoint.

Args
root_url (str): Root url of API. eg.
"https://connection.keboola.com/v2/storage/"
path_component (str): The section of the path specific to the
endpoint. eg. "buckets"
token (str): A key for the Storage API. Can be found in the storage
console.
"""
self.base_url = '{}/{}'.format(root_url.strip('/'),
path_component.strip('/'))
self.token = token

def get(self, *args, **kwargs):
"""
Construct a requests GET call with args and kwargs and process the
results.

Args:
*args: Positional arguments to pass to the get request.
**kwargs: Key word arguments to pass to the get request.

Returns:
body: Response body parsed from json.

Raises:
requests.HTTPError: If the API request fails.
"""
r = requests.get(*args, **kwargs)
try:
r.raise_for_status()
except requests.HTTPError:
# Handle different error codes
raise
else:
return r.json()

def post(self, *args, **kwargs):
"""
Construct a requests POST call with args and kwargs and process the
results.

Args:
*args: Positional arguments to pass to the post request.
**kwargs: Key word arguments to pass to the post request.

Returns:
body: Response body parsed from json.

Raises:
requests.HTTPError: If the API request fails.
"""
r = requests.post(*args, **kwargs)
try:
r.raise_for_status()
except requests.HTTPError:
# Handle different error codes
raise
else:
return r.json()

def put(self):
"""
**Not implemented**

Construct a requests PUT call with args and kwargs and process the
result

Args:
*args: Positional arguments to pass to the put request.
**kwargs: Key word arguments to pass to the put request.

Returns:
body: Response body parsed from json.

Raises:
requests.HTTPError: If the API request fails.
"""
raise NotImplementedError

def delete(self, *args, **kwargs):
"""
Construct a requests DELETE call with args and kwargs and process the
result

Args:
*args: Positional arguments to pass to the delete request.
**kwargs: Key word arguments to pass to the delete request.

Returns:
body: Response body parsed from json.

Raises:
requests.HTTPError: If the API request fails.
"""
r = requests.delete(*args, **kwargs)
try:
r.raise_for_status()
except requests.HTTPError:
# Handle different error codes
raise
# Should delete return something on success?
143 changes: 143 additions & 0 deletions kbcstorage/buckets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
"""
Manages calls to the Storage API relating to buckets

Full documentation `here`.

.. _here:
http://docs.keboola.apiary.io/#reference/buckets/
"""
from kbcstorage.base import Endpoint


class Buckets(Endpoint):
"""
Buckets Endpoint
"""
def __init__(self, root_url, token):
"""
Create a Workspaces endpoint.

Args:
url (:obj:`str`): The base url for the API.
token (:obj:`str`): A storage API key.
"""
super().__init__(root_url, 'buckets', token)

def list(self):
"""
Get all job details.

Returns:
response_body: The parsed json from the HTTP response.

Raises:
requests.HTTPError: If the API request fails.
"""
headers = {'X-StorageApi-Token': self.token}

return self.get(self.base_url, headers=headers)

def detail(self, bucket_id):
"""
Retrieves information about a given bucket.

Args:
bucket_id (int or str): The id of the bucket.

Raises:
requests.HTTPError: If the API request fails.
"""
url = '{}/{}'.format(self.base_url, bucket_id)
headers = {'X-StorageApi-Token': self.token}

return self.get(url, headers=headers)

def create(self, name, stage='in', description='', backend=None):
"""
Create a new bucket.

Args:
name (str): The new bucket name (only alphanumeric and underscores)
stage (str): The new bucket stage. Can be one of ``in`` or ``out``.
Default ``in``.
description (str): The new bucket description.
backend (str): The new bucket backend. Cand be one of
``snowflake``, ``redshift`` or ``mysql``. Default determined by
project settings.
Returns:
response_body: The parsed json from the HTTP response.

Raises:
requests.HTTPError: If the API request fails.
"""
# Separating create and link into two distinct functions...
headers = {
'X-StorageApi-Token': self.token,
'Content-Type': 'application/x-www-form-urlencoded'
}
# Need to check args...
body = {
'name': name,
'stage': stage,
'description': description,
'backend': backend
}

return self.post(self.base_url, headers=headers, data=body)

def delete(self, bucket_id, force=False):
"""
Delete a bucket referenced by ``bucket_id``.

By default, only empty buckets without dependencies (aliases etc) can
be deleted. The optional ``force`` parameter allows for the deletion
of non-empty buckets.

Args:
bucket_id (str or int): The id of the bucket to be deleted.
force (bool): If ``True``, deletes the bucket even if it is not
empty. Default ``False``.
"""
# How does the API handle it when force == False and the bucket is non-
# empty?
url = '{}/{}'.format(self.base_url, bucket_id)
headers = {'X-StorageApi-Token': self.token}
params = {'force': force}
super().delete(url, headers=headers, params=params)

def link(self, *args, **kwargs):
"""
**Not implemented**

Link an existing bucket from another project.

Creates a new bucket which contains the contents of a shared bucket in
a source project. Linking a bucket from another project is only
possible if it has been enabled in the project.
"""
raise NotImplementedError

def share(self, *args, **kwargs):
"""
**Not implemented**

Enable sharing of a bucket.

The bucket will be shared to the entire organisation to which the
project belongs. It may then be shared to any project of that
organization. This operation is only available to administrator tokens.
"""
raise NotImplementedError

def unshare(self, *args, **kwargs):
"""
**Not implemented**

Stop sharing a bucket.

The bucket must not be linked to other projects. To unshare an already
linked bucket, the links must first be deleted - use ``delete`` on the
bucket in the linking project. This operation is only available for
administrator tokens.
"""
raise NotImplementedError
Loading