-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: adds partial users implementation based on lists #14
Closed
+85
−35
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,10 @@ | ||
from .client import Client # noqa | ||
from typing import Optional | ||
|
||
from .client import Client | ||
|
||
|
||
def make_client( | ||
api_key: Optional[str] = None, endpoint: Optional[str] = None | ||
) -> Client: | ||
client = Client(api_key=api_key, endpoint=endpoint) | ||
return client |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,75 @@ | ||
from __future__ import annotations | ||
|
||
import os | ||
|
||
from requests import Session, Response | ||
from dataclasses import dataclass, asdict | ||
from datetime import datetime | ||
from requests import Session | ||
from typing import Optional | ||
|
||
|
||
@dataclass | ||
class User: | ||
guid: str | ||
email: str | ||
username: str | ||
first_name: str | ||
last_name: str | ||
user_role: str | ||
created_time: datetime | ||
updated_time: datetime | ||
active_time: datetime | ||
confirmed: bool | ||
locked: bool | ||
|
||
def to_dict(self) -> dict: | ||
return asdict(self) | ||
|
||
|
||
class Users(list[User]): | ||
"""An extension of :class:`list[User]` with additional fetch methods.""" | ||
|
||
_endpoint: str | ||
_session: Session | ||
|
||
class Users: | ||
def __init__(self, endpoint: str, session: Session) -> None: | ||
def __init__(self, endpoint: str, session: Session): | ||
self._endpoint = endpoint | ||
self._session = session | ||
|
||
def get_user(self, user_id: str) -> Response: | ||
endpoint = os.path.join(self._endpoint, "__api__/v1/users", user_id) | ||
return self._session.get(endpoint) | ||
def find(self, params: dict = {}) -> Users: | ||
"""Finds any :class:`User` that matches the provided filter conditions | ||
|
||
Keyword Arguments: | ||
params -- filter conditions (default: {{}}) | ||
|
||
Returns: | ||
`self` | ||
""" | ||
self.clear() | ||
endpoint = os.path.join(self._endpoint, "__api__/v1/users") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see this pattern in a few places, might want to make some URL constructor method for Client or something, which handles the |
||
response = self._session.get(endpoint) | ||
data = response.json() | ||
for user in data["results"]: | ||
if all(user.get(k) == v for k, v in params.items()): | ||
self.append(User(**user)) | ||
# todo - implement paging and caching | ||
return self | ||
|
||
def find_one(self, params: dict = {}) -> Optional[User]: | ||
"""Finds one :class:`User` | ||
|
||
Keyword Arguments: | ||
params -- filter conditions (default: {{}}) | ||
|
||
Returns: | ||
A matching :class:`User`. | ||
""" | ||
if "guid" in params: | ||
# Use the user details API if a 'guid' is provided. | ||
# This is an example of how we can use different API endpoints to optimize execution time. | ||
endpoint = os.path.join(self._endpoint, "__api__/v1/users", params["guid"]) | ||
response = self._session.get(endpoint) | ||
return User(**response.json()) | ||
|
||
def get_current_user(self) -> Response: | ||
endpoint = os.path.join(self._endpoint, "__api__/v1/user") | ||
return self._session.get(endpoint) | ||
# Otherwise, perform a normal search. | ||
return next(iter(self.find(params)), None) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +0,0 @@ | ||
from unittest.mock import Mock | ||
|
||
from .users import Users | ||
|
||
|
||
class TestUsers: | ||
def test_get_user(self): | ||
session = Mock() | ||
session.get = Mock(return_value={}) | ||
users = Users(endpoint="http://foo.bar/", session=session) | ||
response = users.get_user(user_id="foo") | ||
assert response == {} | ||
session.get.assert_called_once_with("http://foo.bar/__api__/v1/users/foo") | ||
|
||
def test_get_current_user(self): | ||
session = Mock() | ||
session.get = Mock(return_value={}) | ||
users = Users(endpoint="http://foo.bar/", session=session) | ||
response = users.get_current_user() | ||
assert response == {} | ||
session.get.assert_called_once_with("http://foo.bar/__api__/v1/user") | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
from posit.connect.client import Client | ||
from posit.connect import make_client | ||
|
||
client = Client() | ||
res = client.users.get_current_user() | ||
print(res.json()) | ||
client = make_client() | ||
for user in client.users.find({"username": "aaron"}): | ||
print(user) | ||
|
||
print(client.users.find_one()) | ||
|
||
print(client.users.find_one({"guid": "f155520a-ca2e-4084-b0a0-12120b7d1add"})) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's this for? how is
make_client()
different fromClient()
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Methods are slightly more "pythonic"/"idiomatic." And this is what boto3 does, so hopefully, it is more familiar to the average user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not seeing this.
What libraries are you thinking of where this is common?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
boto3.client('s3')
andboto3.resource('s3')
is what I'm referring to. https://github.com/boto/boto3/blob/develop/boto3/__init__.py#L86-L101Both are factory methods which return a new instance based on the provided service_name.