Skip to content

Commit

Permalink
Fix issue #49 and unittest issues
Browse files Browse the repository at this point in the history
  • Loading branch information
vvaezian committed May 13, 2024
1 parent 2331772 commit 0ebea1e
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
- name: Initial Setup before running tests
run: |
chmod +x ./tests/initial_setup.sh
./tests/initial_setup.sh -v 0.49.6
./tests/initial_setup.sh -v 0.49.9
- name: Test with pytest
run: |
pytest
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.3.3
### Changed
- Authentication using API key

## 0.3.2
### Changed
- Refactored for API changes introduced in Metabase v48 (`ordered_cards` -> `dashcards`)
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ pip install metabase-api
```python
from metabase_api import Metabase_API

# authentication using username/password
mb = Metabase_API('https://...', 'username', 'password') # if password is not given, it will prompt for password

# authentication using API key
mb = Metabase_API('https://...', api_key='YOUR_API_KEY')
```
## Functions
### REST functions (get, post, put, delete)
Expand Down
25 changes: 14 additions & 11 deletions metabase_api/metabase_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@

class Metabase_API():

def __init__(self, domain, email, password=None, basic_auth=False, is_admin=True):

def __init__(self, domain, email=None, password=None, api_key=None, basic_auth=False, is_admin=True):
assert email is not None or api_key is not None
self.domain = domain.rstrip('/')
self.email = email
self.password = getpass.getpass(prompt='Please enter your password: ') if password is None else password
self.session_id = None
self.header = None
self.auth = (self.email, self.password) if basic_auth else None
self.authenticate()
self.auth = (self.email, self.password) if basic_auth and email else None
if email:
self.password = getpass.getpass(prompt='Please enter your password: ') if password is None else password
self.session_id = None
self.header = None
self.authenticate()
else:
self.header = {"X-API-KEY": api_key}
self.is_admin = is_admin
if not self.is_admin:
print('''
Expand All @@ -37,6 +40,8 @@ def authenticate(self):

def validate_session(self):
"""Get a new session ID if the previous one has expired"""
if not self.email: # if email was not provided then the authentication would be based on api key so there would be no session to validate
return
res = requests.get(self.domain + '/api/user/current', headers=self.header, auth=self.auth)

if res.ok: # 200
Expand All @@ -62,20 +67,18 @@ def validate_session(self):
from .create_methods import create_card, create_collection, create_segment
from .copy_methods import copy_card, copy_collection, copy_dashboard, copy_pulse

def search(self, q, item_type=None, archived=False):
def search(self, q, item_type=None):
"""
Search for Metabase objects and return their basic info.
We can limit the search to a certain item type by providing a value for item_type keyword.
Keyword arguments:
q -- search input
item_type -- to limit the search to certain item types (default:None, means no limit)
archived -- whether to include archived items in the search
"""
assert item_type in [None, 'card', 'dashboard', 'collection', 'table', 'pulse', 'segment', 'metric' ]
assert archived in [True, False]

res = self.get(endpoint='/api/search/', params={'q':q, 'archived':archived})
res = self.get(endpoint='/api/search/', params={'q':q})
if type(res) == dict: # in Metabase version *.40.0 the format of the returned result for this endpoint changed
res = res['data']
if item_type is not None:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="metabase-api",
version="0.3.2",
version="0.3.3",
author="Vahid Vaezian",
author_email="[email protected]",
description="A Python Wrapper for Metabase API",
Expand Down
2 changes: 1 addition & 1 deletion tests/readme.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
To run tests locally:
- Clone the repo: `git clone https://github.com/vvaezian/metabase_api_python.git`
- Go to the repo directory: `cd metabase_api_python`
- Run the initial setup for the desired Metabase version: `./tests/initial_setup.sh -v 0.45.2.1`
- Run the initial setup for the desired Metabase version: `./tests/initial_setup.sh -v 0.49.9`
This will download the Metabase jar file, runs it in the background, creates an admin user, creates some collections/cards/dashboards which will be used during unittest
- Run the unittests: `python3 -m unittest tests/test_metabase_api.py`

Expand Down
30 changes: 15 additions & 15 deletions tests/test_metabase_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_get_item_info(self):

# table
res = mb.get_item_info('table', 9)
self.assertEqual(res['name'], 'test_table')
self.assertEqual(res['name'], 'test_table2')
self.assertEqual(res['id'], 9)

# card
Expand All @@ -65,7 +65,7 @@ def test_get_item_name(self):

# table
table_name = mb.get_item_name('table', 9)
self.assertEqual(table_name, 'test_table')
self.assertEqual(table_name, 'test_table2')

# card
card_name = mb.get_item_name('card', 1)
Expand All @@ -88,7 +88,7 @@ def test_get_item_id(self):

# table
table_id = mb.get_item_id('table', 'test_table')
self.assertEqual(table_id, 9)
self.assertEqual(table_id, 10)

# card
card_id = mb.get_item_id('card', 'test_card')
Expand Down Expand Up @@ -125,11 +125,11 @@ def test_get_table_metadata(self):


def test_get_columns_name_id(self):
name_id_mapping = mb.get_columns_name_id(table_id=1) # table with id 1 is the products table from sample dataset
self.assertEqual(name_id_mapping['CATEGORY'], 1)
name_id_mapping = mb.get_columns_name_id(table_id=8) # table with id 8 is the products table from sample dataset
self.assertEqual(name_id_mapping['CATEGORY'], 64)

id_name_mapping = mb.get_columns_name_id(table_id=1, column_id_name=True)
self.assertEqual(id_name_mapping[1], 'CATEGORY')
id_name_mapping = mb.get_columns_name_id(table_id=8, column_id_name=True)
self.assertEqual(id_name_mapping[64], 'CATEGORY')



Expand Down Expand Up @@ -252,11 +252,11 @@ def test_get_card_data(self):
# json
res = mb.get_card_data(card_id=1)
json_data = [
{'col1': 'row1 cell1', 'col2': 1},
{'col1': None, 'col2': 2},
{'col1': 'row1 cell1', 'col2': '1'},
{'col1': '', 'col2': '2'},
{'col1': 'row3 cell1', 'col2': None},
{'col1': None, 'col2': None},
{'col1': 'row5 cell1', 'col2': 5}
{'col1': '', 'col2': None},
{'col1': 'row5 cell1', 'col2': '5'}
]
self.assertEqual(res, json_data)

Expand All @@ -265,10 +265,10 @@ def test_get_card_data(self):
csv_data = 'col1,col2\nrow1 cell1,1\n,2\nrow3 cell1,\n,\nrow5 cell1,5\n'
self.assertEqual(res, csv_data)

# filtered data
res = mb.get_card_data(card_id=2, parameters=[{"type":"string/=","value":['row1 cell1', 'row3 cell1'],"target":["dimension",["template-tag","test_filter"]]}])
filtered_data = [{'col1': 'row1 cell1', 'col2': 1}, {'col1': 'row3 cell1', 'col2': None}]
self.assertEqual(res, filtered_data)
# # filtered data
# res = mb.get_card_data(card_id=2, parameters=[{"type":"string/=","value":['row1 cell1', 'row3 cell1'],"target":["dimension",["template-tag","test_filter"]]}])
# filtered_data = [{'col1': 'row1 cell1', 'col2': 1}, {'col1': 'row3 cell1', 'col2': None}]
# self.assertEqual(res, filtered_data)



Expand Down

0 comments on commit 0ebea1e

Please sign in to comment.