from typing import Any, Optional
from uuid import UUID
from shared.api.models import (
WrappedAPIKeyResponse,
WrappedAPIKeysResponse,
WrappedBooleanResponse,
WrappedCollectionsResponse,
WrappedGenericMessageResponse,
WrappedLimitsResponse,
WrappedLoginResponse,
WrappedTokenResponse,
WrappedUserResponse,
WrappedUsersResponse,
)
class UsersSDK:
def __init__(self, client):
self.client = client
def create(
self,
email: str,
password: str,
name: Optional[str] = None,
bio: Optional[str] = None,
profile_picture: Optional[str] = None,
) -> WrappedUserResponse:
"""Register a new user.
Args:
email (str): User's email address
password (str): User's password
name (Optional[str]): The name for the new user
bio (Optional[str]): The bio for the new user
profile_picture (Optional[str]): New user profile picture
Returns:
UserResponse: New user information
"""
data: dict = {"email": email, "password": password}
if name is not None:
data["name"] = name
if bio is not None:
data["bio"] = bio
if profile_picture is not None:
data["profile_picture"] = profile_picture
response_dict = self.client._make_request(
"POST",
"users",
json=data,
version="v3",
)
return WrappedUserResponse(**response_dict)
def send_verification_email(
self, email: str
) -> WrappedGenericMessageResponse:
"""Request that a verification email to a user."""
response_dict = self.client._make_request(
"POST",
"users/send-verification-email",
json=email,
version="v3",
)
return WrappedGenericMessageResponse(**response_dict)
def delete(self, id: str | UUID, password: str) -> WrappedBooleanResponse:
"""Delete a specific user. Users can only delete their own account
unless they are superusers.
Args:
id (str | UUID): User ID to delete
password (str): User's password
Returns:
dict: Deletion result
"""
data: dict[str, Any] = {"password": password}
response_dict = self.client._make_request(
"DELETE",
f"users/{str(id)}",
json=data,
version="v3",
)
self.client.access_token = None
self.client._refresh_token = None
return WrappedBooleanResponse(**response_dict)
def verify_email(
self, email: str, verification_code: str
) -> WrappedGenericMessageResponse:
"""Verify a user's email address.
Args:
email (str): User's email address
verification_code (str): Verification code sent to the user's email
Returns:
dict: Verification result
"""
data: dict[str, Any] = {
"email": email,
"verification_code": verification_code,
}
response_dict = self.client._make_request(
"POST",
"users/verify-email",
json=data,
version="v3",
)
return WrappedGenericMessageResponse(**response_dict)
def login(self, email: str, password: str) -> WrappedLoginResponse:
"""Log in a user.
Args:
email (str): User's email address
password (str): User's password
Returns:
WrappedLoginResponse
"""
if self.client.api_key:
raise ValueError(
"Cannot log in after setting an API key, please unset your R2R_API_KEY variable or call client.set_api_key(None)"
)
data: dict[str, Any] = {"username": email, "password": password}
response_dict = self.client._make_request(
"POST",
"users/login",
data=data,
version="v3",
)
login_response = WrappedLoginResponse(**response_dict)
self.client.access_token = login_response.results.access_token.token
self.client._refresh_token = login_response.results.refresh_token.token
user = self.client._make_request(
"GET",
"users/me",
version="v3",
)
user_response = WrappedUserResponse(**user)
self.client._user_id = user_response.results.id
return login_response
def logout(self) -> WrappedGenericMessageResponse | None:
"""Log out the current user."""
if self.client.access_token:
response_dict = self.client._make_request(
"POST",
"users/logout",
version="v3",
)
self.client.access_token = None
self.client._refresh_token = None
return WrappedGenericMessageResponse(**response_dict)
self.client.access_token = None
self.client._refresh_token = None
return None
def refresh_token(self) -> WrappedTokenResponse:
"""Refresh the access token using the refresh token."""
if self.client._refresh_token:
response_dict = self.client._make_request(
"POST",
"users/refresh-token",
json=self.client._refresh_token,
version="v3",
)
self.client.access_token = response_dict["results"]["access_token"][
"token"
]
self.client._refresh_token = response_dict["results"]["refresh_token"][
"token"
]
return WrappedTokenResponse(**response_dict)
def change_password(
self, current_password: str, new_password: str
) -> WrappedGenericMessageResponse:
"""Change the user's password.
Args:
current_password (str): User's current password
new_password (str): User's new password
Returns:
dict: Change password result
"""
data: dict[str, Any] = {
"current_password": current_password,
"new_password": new_password,
}
response_dict = self.client._make_request(
"POST",
"users/change-password",
json=data,
version="v3",
)
return WrappedGenericMessageResponse(**response_dict)
def request_password_reset(
self, email: str
) -> WrappedGenericMessageResponse:
"""Request a password reset.
Args:
email (str): User's email address
Returns:
dict: Password reset request result
"""
response_dict = self.client._make_request(
"POST",
"users/request-password-reset",
json=email,
version="v3",
)
return WrappedGenericMessageResponse(**response_dict)
def reset_password(
self, reset_token: str, new_password: str
) -> WrappedGenericMessageResponse:
"""Reset password using a reset token.
Args:
reset_token (str): Password reset token
new_password (str): New password
Returns:
dict: Password reset result
"""
data: dict[str, Any] = {
"reset_token": reset_token,
"new_password": new_password,
}
response_dict = self.client._make_request(
"POST",
"users/reset-password",
json=data,
version="v3",
)
return WrappedGenericMessageResponse(**response_dict)
def list(
self,
ids: Optional[list[str | UUID]] = None,
offset: Optional[int] = 0,
limit: Optional[int] = 100,
) -> WrappedUsersResponse:
"""List users with pagination and filtering options.
Args:
offset (int, optional): Specifies the number of objects to skip. Defaults to 0.
limit (int, optional): Specifies a limit on the number of objects to return, ranging between 1 and 100. Defaults to 100.
Returns:
dict: List of users and pagination information
"""
params = {
"offset": offset,
"limit": limit,
}
if ids:
params["ids"] = [str(user_id) for user_id in ids] # type: ignore
response_dict = self.client._make_request(
"GET",
"users",
params=params,
version="v3",
)
return WrappedUsersResponse(**response_dict)
def retrieve(
self,
id: str | UUID,
) -> WrappedUserResponse:
"""Get a specific user.
Args:
id (str | UUID): User ID to retrieve
Returns:
dict: Detailed user information
"""
response_dict = self.client._make_request(
"GET",
f"users/{str(id)}",
version="v3",
)
return WrappedUserResponse(**response_dict)
def me(
self,
) -> WrappedUserResponse:
"""Get detailed information about the currently authenticated user.
Returns:
dict: Detailed user information
"""
response_dict = self.client._make_request(
"GET",
"users/me",
version="v3",
)
return WrappedUserResponse(**response_dict)
def update(
self,
id: str | UUID,
email: Optional[str] = None,
is_superuser: Optional[bool] = None,
name: Optional[str] = None,
bio: Optional[str] = None,
profile_picture: Optional[str] = None,
limits_overrides: dict | None = None,
metadata: dict[str, str | None] | None = None,
) -> WrappedUserResponse:
"""Update user information.
Args:
id (str | UUID): User ID to update
username (Optional[str]): New username
is_superuser (Optional[bool]): Update superuser status
name (Optional[str]): New name
bio (Optional[str]): New bio
profile_picture (Optional[str]): New profile picture
Returns:
dict: Updated user information
"""
data: dict = {}
if email is not None:
data["email"] = email
if is_superuser is not None:
data["is_superuser"] = is_superuser
if name is not None:
data["name"] = name
if bio is not None:
data["bio"] = bio
if profile_picture is not None:
data["profile_picture"] = profile_picture
if limits_overrides is not None:
data["limits_overrides"] = limits_overrides
if metadata is not None:
data["metadata"] = metadata
response_dict = self.client._make_request(
"POST",
f"users/{str(id)}",
json=data,
version="v3",
)
return WrappedUserResponse(**response_dict)
def list_collections(
self,
id: str | UUID,
offset: Optional[int] = 0,
limit: Optional[int] = 100,
) -> WrappedCollectionsResponse:
"""Get all collections associated with a specific user.
Args:
id (str | UUID): User ID to get collections for
offset (int, optional): Specifies the number of objects to skip. Defaults to 0.
limit (int, optional): Specifies a limit on the number of objects to return, ranging between 1 and 100. Defaults to 100.
Returns:
dict: List of collections and pagination information
"""
params = {
"offset": offset,
"limit": limit,
}
response_dict = self.client._make_request(
"GET",
f"users/{str(id)}/collections",
params=params,
version="v3",
)
return WrappedCollectionsResponse(**response_dict)
def add_to_collection(
self,
id: str | UUID,
collection_id: str | UUID,
) -> WrappedBooleanResponse:
"""Add a user to a collection.
Args:
id (str | UUID): User ID to add
collection_id (str | UUID): Collection ID to add user to
"""
response_dict = self.client._make_request(
"POST",
f"users/{str(id)}/collections/{str(collection_id)}",
version="v3",
)
return WrappedBooleanResponse(**response_dict)
def remove_from_collection(
self,
id: str | UUID,
collection_id: str | UUID,
) -> WrappedBooleanResponse:
"""Remove a user from a collection.
Args:
id (str | UUID): User ID to remove
collection_id (str | UUID): Collection ID to remove user from
Returns:
bool: True if successful
"""
response_dict = self.client._make_request(
"DELETE",
f"users/{str(id)}/collections/{str(collection_id)}",
version="v3",
)
return WrappedBooleanResponse(**response_dict)
def create_api_key(
self,
id: str | UUID,
name: Optional[str] = None,
description: Optional[str] = None,
) -> WrappedAPIKeyResponse:
"""Create a new API key for the specified user.
Args:
id (str | UUID): User ID to create API key for
name (Optional[str]): Name of the API key
description (Optional[str]): Description of the API key
Returns:
dict: { "message": "API key created successfully", "api_key": "key_id.raw_api_key" }
"""
data: dict[str, Any] = {}
if name:
data["name"] = name
if description:
data["description"] = description
response_dict = self.client._make_request(
"POST",
f"users/{str(id)}/api-keys",
json=data,
version="v3",
)
return WrappedAPIKeyResponse(**response_dict)
def list_api_keys(
self,
id: str | UUID,
) -> WrappedAPIKeysResponse:
"""List all API keys for the specified user.
Args:
id (str | UUID): User ID to list API keys for
Returns:
WrappedAPIKeysResponse
"""
resp_dict = self.client._make_request(
"GET",
f"users/{str(id)}/api-keys",
version="v3",
)
return WrappedAPIKeysResponse(**resp_dict)
def delete_api_key(
self,
id: str | UUID,
key_id: str | UUID,
) -> WrappedBooleanResponse:
"""Delete a specific API key for the specified user.
Args:
id (str | UUID): User ID
key_id (str | UUID): API key ID to delete
Returns:
dict: { "message": "API key deleted successfully" }
"""
response_dict = self.client._make_request(
"DELETE",
f"users/{str(id)}/api-keys/{str(key_id)}",
version="v3",
)
return WrappedBooleanResponse(**response_dict)
def get_limits(self) -> WrappedLimitsResponse:
response_dict = self.client._make_request(
"GET",
f"users/{str(self.client._user_id)}/limits",
version="v3",
)
return WrappedLimitsResponse(**response_dict)
def oauth_google_authorize(self) -> WrappedGenericMessageResponse:
"""Get Google OAuth 2.0 authorization URL from the server.
Returns:
WrappedGenericMessageResponse
"""
response_dict = self.client._make_request(
"GET",
"users/oauth/google/authorize",
version="v3",
)
return WrappedGenericMessageResponse(**response_dict)
def oauth_github_authorize(self) -> WrappedGenericMessageResponse:
"""Get GitHub OAuth 2.0 authorization URL from the server.
Returns: {"redirect_url": "..."}
"""
response_dict = self.client._make_request(
"GET",
"users/oauth/github/authorize",
version="v3",
)
return WrappedGenericMessageResponse(**response_dict)
def oauth_google_callback(
self, code: str, state: str
) -> WrappedLoginResponse:
"""Exchange `code` and `state` with the Google OAuth 2.0 callback
route."""
response_dict = self.client._make_request(
"GET",
"users/oauth/google/callback",
params={"code": code, "state": state},
version="v3",
)
return WrappedLoginResponse(**response_dict)
def oauth_github_callback(
self, code: str, state: str
) -> WrappedLoginResponse:
"""Exchange `code` and `state` with the GitHub OAuth 2.0 callback
route."""
response_dict = self.client._make_request(
"GET",
"users/oauth/github/callback",
params={"code": code, "state": state},
version="v3",
)
return WrappedLoginResponse(**response_dict)