Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions src/metabase/resources/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,39 @@ class User(ListResource, CreateResource, GetResource, UpdateResource, DeleteReso
updated_at: datetime

@classmethod
def list(cls, using: Metabase) -> List[User]:
def list(
cls,
using: Metabase,
status: str = None,
query: str = None,
group_id: int = None,
include_deactivated: bool = None,
limit: int = None,
offset: int = None,
) -> List[User]:
"""
Fetch a list of Users. By default returns every active user but only active users.

If status is deactivated, include deactivated users only. If status is all, include all users (active and inactive). Also supports include_deactivated, which if true, is equivalent to status=all. status and included_deactivated requires superuser permissions.
If status is deactivated, include deactivated users only. If status is all, include all users (active and
inactive). Also supports include_deactivated, which if true, is equivalent to status=all. status and
included_deactivated requires superuser permissions.

For users with segmented permissions, return only themselves.

Takes limit, offset for pagination. Takes query for filtering on first name, last name, email. Also takes group_id, which filters on group id.
Takes limit, offset for pagination. Takes query for filtering on first name, last name, email. Also takes
group_id, which filters on group id.
"""
response = using.get(cls.ENDPOINT)
response = using.get(
cls.ENDPOINT,
params={
"status": status,
"query": query,
"group_id": group_id,
"include_deactivated": include_deactivated,
"limit": limit,
"offset": offset,
},
)
records = [
cls(_using=using, **user) for user in response.json().get("data", [])
]
Expand Down
50 changes: 50 additions & 0 deletions tests/resources/test_user.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from random import randint

from metabase import PermissionGroup
from metabase.exceptions import NotFoundError
from metabase.resources.user import User
from tests.helpers import IntegrationTestCase
Expand All @@ -12,12 +13,61 @@ def tearDown(self) -> None:
if user.id != 1:
user.delete()

groups = PermissionGroup.list(using=self.metabase)
for group in groups:
if group.id > 2:
group.delete()

def test_import(self):
"""Ensure User can be imported from Metabase."""
from metabase import User

self.assertIsNotNone(User(_using=None))

def test_list(self):
"""Ensure User.list() returns a list of Users, and supports filter parameters."""
users = User.list(using=self.metabase)
self.assertIsInstance(users, list)
self.assertEqual(1, len(users))

user1 = User.create(
first_name="Test",
last_name="Test",
email=f"{randint(2, 10000)}@example.com",
password="example123",
using=self.metabase,
)
group = PermissionGroup.create(using=self.metabase, name="foo")
user2 = User.create(
first_name="Test",
last_name="Test",
email=f"{randint(2, 10000)}@example.com",
password="example123",
group_ids=[1, group.id],
using=self.metabase,
)

users = User.list(using=self.metabase)
self.assertEqual(3, len(users))

users = User.list(using=self.metabase, query=user1.email)
self.assertEqual(1, len(users))
self.assertEqual(users[0].id, user1.id)

users = User.list(using=self.metabase, group_id=group.id)
self.assertEqual(1, len(users))
self.assertEqual(users[0].id, user2.id)

user1.delete()
users = User.list(using=self.metabase, include_deactivated=True)
self.assertTrue(user1.id in map(lambda u: u.id, users))

users = User.list(using=self.metabase, limit=1)
self.assertEqual(1, len(users))

users = User.list(using=self.metabase, limit=2)
self.assertEqual(2, len(users))

def test_get(self):
"""
Ensure User.get() returns a User instance for a given ID, or
Expand Down