Skip to content

Commit 038f67a

Browse files
authored
Merge pull request #28 from keboola/dev
A completely new version
2 parents 3b1ac1b + 86c41e0 commit 038f67a

22 files changed

Lines changed: 2542 additions & 263 deletions

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ services:
55
before_script:
66
- docker-compose build sapi-python-client
77
script:
8-
- docker-compose run --rm --entrypoint=flake8 sapi-python-client || true
9-
- docker-compose run --rm -e KBC_TEST_TOKEN sapi-python-client -m unittest discover
8+
- docker-compose run --rm --entrypoint=flake8 sapi-python-client
9+
- docker-compose run --rm -e KBC_TEST_TOKEN -e KBC_TEST_API_URL sapi-python-client -m unittest discover
1010
after_success:
1111
- docker images
1212
deploy:

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ FROM python:3.6
22

33
WORKDIR /code
44
COPY . /code/
5-
RUN pip3 install --no-cache-dir flake8
5+
RUN pip3 install --no-cache-dir flake8 responses
66
RUN python setup.py install
77
ENTRYPOINT ["python"]

README.md

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
[![Build Status](https://travis-ci.org/keboola/sapi-python-client.svg?branch=master)](https://travis-ci.org/keboola/sapi-python-client)
22

33
# Python client for the Keboola Storage API
4+
Client for using [Keboola Connection Storage API](http://docs.keboola.apiary.io/). This API client provides client methods to get data from KBC and store data in KBC. The endpoints
5+
for working with buckets, tables and workspaces are covered.
46

57
## Install
68

7-
`$ pip install git+https://github.com/keboola/sapi-python-client.git`
9+
`$ pip3 install git+https://github.com/keboola/sapi-python-client.git`
810

911
or
1012

@@ -15,24 +17,26 @@ $ python setup.py install
1517

1618
## Usage
1719
```
18-
from kbcstorage.client import Client
20+
from kbcstorage.tables import Tables
21+
from kbcstorage.buckets import Buckets
1922
20-
cl = Client("MY_KBC_TOKEN")
23+
tables = Tables('https://connection.keboola.com', 'your-token')
2124
2225
# get table data into local file
23-
cl.get_table_data("in.c-myBucket.myTable", "local_file_name.csv')
26+
tables.export_to_file(table_id='in.c-demo.some-table', path_name='/data/')
2427
2528
# save data
26-
cl.save_table("tableName", "in.c-myBucket", "csv_I_want_to_store.csv")
29+
tables.create(name='some-table-2', bucket_id='in.c-demo', file_path='/data/some-table')
2730
2831
# list buckets
29-
cl.list_buckets()
32+
buckets = Buckets('https://connection.keboola.com', 'your-token')
33+
buckets.list()
3034
3135
# list bucket tables
32-
cl.list_tables(bucketId)
36+
buckets.list_tables('in.c-demo')
3337
3438
# get table info
35-
cl.get_table(tableId)
39+
tables.detail('in.c-demo')
3640
3741
```
3842

@@ -43,6 +47,19 @@ Docker image with pre-installed library is also available, run it via:
4347
docker run -i -t quay.io/keboola/sapi-python-client
4448
```
4549

50+
## Tests
51+
52+
```bash
53+
$ git clone https://github.com/keboola/sapi-python-client.git && cd sapi-python-client
54+
$ python setup.py test
55+
```
56+
57+
or
58+
59+
```bash
60+
$ docker-compose run --rm -e KBC_TEST_TOKEN -e KBC_TEST_API_URL sapi-python-client -m unittest discover
61+
```
62+
4663
Under development -- all contributions very welcome :)
4764

4865
Kickstarted via https://gist.github.com/Halama/6006960

kbcstorage/base.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"""
2+
Base classes for constructing the client.
3+
4+
Primarily exposes a base Endpoint class which deduplicates functionality across
5+
various endpoints, such as tables, workspaces, jobs, etc. as described in the
6+
`Storage API documentation`.
7+
8+
9+
.. _Storage API documentation:
10+
http://docs.keboola.apiary.io/
11+
"""
12+
import requests
13+
14+
15+
class Endpoint:
16+
"""
17+
Base class for implementing a single endpoint related to a single entities
18+
as described in the Storage API.
19+
20+
Attributes:
21+
base_url (str): The base URL for this endpoint.
22+
token (str): A key for the Storage API.
23+
"""
24+
def __init__(self, root_url, path_component, token):
25+
"""
26+
Create an endpoint.
27+
28+
Args
29+
root_url (str): Root url of API. eg.
30+
"https://connection.keboola.com/v2/storage/"
31+
path_component (str): The section of the path specific to the
32+
endpoint. eg. "buckets"
33+
token (str): A key for the Storage API. Can be found in the storage
34+
console.
35+
"""
36+
self.root_url = root_url
37+
self.base_url = '{}/v2/storage/{}'.format(root_url.strip('/'),
38+
path_component.strip('/'))
39+
self.token = token
40+
41+
def get(self, *args, **kwargs):
42+
"""
43+
Construct a requests GET call with args and kwargs and process the
44+
results.
45+
46+
Args:
47+
*args: Positional arguments to pass to the get request.
48+
**kwargs: Key word arguments to pass to the get request.
49+
50+
Returns:
51+
body: Response body parsed from json.
52+
53+
Raises:
54+
requests.HTTPError: If the API request fails.
55+
"""
56+
r = requests.get(*args, **kwargs)
57+
try:
58+
r.raise_for_status()
59+
except requests.HTTPError:
60+
# Handle different error codes
61+
raise
62+
else:
63+
return r.json()
64+
65+
def post(self, *args, **kwargs):
66+
"""
67+
Construct a requests POST call with args and kwargs and process the
68+
results.
69+
70+
Args:
71+
*args: Positional arguments to pass to the post request.
72+
**kwargs: Key word arguments to pass to the post request.
73+
74+
Returns:
75+
body: Response body parsed from json.
76+
77+
Raises:
78+
requests.HTTPError: If the API request fails.
79+
"""
80+
r = requests.post(*args, **kwargs)
81+
try:
82+
r.raise_for_status()
83+
except requests.HTTPError:
84+
# Handle different error codes
85+
raise
86+
else:
87+
return r.json()
88+
89+
def delete(self, *args, **kwargs):
90+
"""
91+
Construct a requests DELETE call with args and kwargs and process the
92+
result
93+
94+
Args:
95+
*args: Positional arguments to pass to the delete request.
96+
**kwargs: Key word arguments to pass to the delete request.
97+
98+
Returns:
99+
body: Response body parsed from json.
100+
101+
Raises:
102+
requests.HTTPError: If the API request fails.
103+
"""
104+
r = requests.delete(*args, **kwargs)
105+
try:
106+
r.raise_for_status()
107+
except requests.HTTPError:
108+
# Handle different error codes
109+
raise
110+
# Should delete return something on success?

kbcstorage/buckets.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"""
2+
Manages calls to the Storage API relating to buckets
3+
4+
Full documentation `here`.
5+
6+
.. _here:
7+
http://docs.keboola.apiary.io/#reference/buckets/
8+
"""
9+
from kbcstorage.base import Endpoint
10+
11+
12+
class Buckets(Endpoint):
13+
"""
14+
Buckets Endpoint
15+
"""
16+
def __init__(self, root_url, token):
17+
"""
18+
Create a Buckets endpoint.
19+
20+
Args:
21+
root_url (:obj:`str`): The base url for the API.
22+
token (:obj:`str`): A storage API key.
23+
"""
24+
super().__init__(root_url, 'buckets', token)
25+
26+
def list(self):
27+
"""
28+
List all buckets in project.
29+
30+
Returns:
31+
response_body: The parsed json from the HTTP response.
32+
33+
Raises:
34+
requests.HTTPError: If the API request fails.
35+
"""
36+
headers = {'X-StorageApi-Token': self.token}
37+
38+
return self.get(self.base_url, headers=headers)
39+
40+
def list_tables(self, bucket_id, include=None):
41+
"""
42+
List all tables in a bucket.
43+
44+
Args:
45+
bucket_id (str): Id of the bucket
46+
include (list): Properties to list (attributes, columns)
47+
Returns:
48+
response_body: The parsed json from the HTTP response.
49+
50+
Raises:
51+
requests.HTTPError: If the API request fails.
52+
"""
53+
headers = {'X-StorageApi-Token': self.token}
54+
55+
url = '{}/{}/tables'.format(self.base_url, bucket_id)
56+
params = {}
57+
if include is not None and isinstance(include, list):
58+
params['include'] = ','.join(include)
59+
return self.get(url, headers=headers, params=params)
60+
61+
def detail(self, bucket_id):
62+
"""
63+
Retrieves information about a given bucket.
64+
65+
Args:
66+
bucket_id (str): The id of the bucket.
67+
68+
Raises:
69+
requests.HTTPError: If the API request fails.
70+
"""
71+
url = '{}/{}'.format(self.base_url, bucket_id)
72+
headers = {'X-StorageApi-Token': self.token}
73+
74+
return self.get(url, headers=headers)
75+
76+
def create(self, name, stage='in', description='', backend=None):
77+
"""
78+
Create a new bucket.
79+
80+
Args:
81+
name (str): The new bucket name (only alphanumeric and underscores)
82+
stage (str): The new bucket stage. Can be one of ``in`` or ``out``.
83+
Default ``in``.
84+
description (str): The new bucket description.
85+
backend (str): The new bucket backend. Cand be one of
86+
``snowflake``, ``redshift`` or ``mysql``. Default determined by
87+
project settings.
88+
Returns:
89+
response_body: The parsed json from the HTTP response.
90+
91+
Raises:
92+
requests.HTTPError: If the API request fails.
93+
"""
94+
# Separating create and link into two distinct functions...
95+
headers = {
96+
'X-StorageApi-Token': self.token,
97+
'Content-Type': 'application/x-www-form-urlencoded'
98+
}
99+
# Need to check args...
100+
body = {
101+
'name': name,
102+
'stage': stage,
103+
'description': description,
104+
'backend': backend
105+
}
106+
107+
return self.post(self.base_url, headers=headers, data=body)
108+
109+
def delete(self, bucket_id, force=False):
110+
"""
111+
Delete a bucket referenced by ``bucket_id``.
112+
113+
By default, only empty buckets without dependencies (aliases etc) can
114+
be deleted. The optional ``force`` parameter allows for the deletion
115+
of non-empty buckets.
116+
117+
Args:
118+
bucket_id (str): The id of the bucket to be deleted.
119+
force (bool): If ``True``, deletes the bucket even if it is not
120+
empty. Default ``False``.
121+
"""
122+
# How does the API handle it when force == False and the bucket is non-
123+
# empty?
124+
url = '{}/{}'.format(self.base_url, bucket_id)
125+
headers = {'X-StorageApi-Token': self.token}
126+
params = {'force': force}
127+
super().delete(url, headers=headers, params=params)
128+
129+
def link(self, *args, **kwargs):
130+
"""
131+
**Not implemented**
132+
133+
Link an existing bucket from another project.
134+
135+
Creates a new bucket which contains the contents of a shared bucket in
136+
a source project. Linking a bucket from another project is only
137+
possible if it has been enabled in the project.
138+
"""
139+
raise NotImplementedError
140+
141+
def share(self, *args, **kwargs):
142+
"""
143+
**Not implemented**
144+
145+
Enable sharing of a bucket.
146+
147+
The bucket will be shared to the entire organisation to which the
148+
project belongs. It may then be shared to any project of that
149+
organization. This operation is only available to administrator tokens.
150+
"""
151+
raise NotImplementedError
152+
153+
def unshare(self, *args, **kwargs):
154+
"""
155+
**Not implemented**
156+
157+
Stop sharing a bucket.
158+
159+
The bucket must not be linked to other projects. To unshare an already
160+
linked bucket, the links must first be deleted - use ``delete`` on the
161+
bucket in the linking project. This operation is only available for
162+
administrator tokens.
163+
"""
164+
raise NotImplementedError

0 commit comments

Comments
 (0)