From 809296d1b23aaf80880086afc6adb8b36c736aba Mon Sep 17 00:00:00 2001 From: "Skhendle@gmail.com" Date: Fri, 30 Jan 2026 15:38:37 +0200 Subject: [PATCH 1/3] completed roles collect --- examples/company_user_roles_demo.py | 275 ++++++++++++++++++ examples/company_user_roles_demo_README.md | 118 ++++++++ .../clients/company_user_roles_management.py | 112 +++++++ tests/conftest.py | 50 ++++ ...st_company_user_roles_management_client.py | 160 ++++++++++ 5 files changed, 715 insertions(+) create mode 100644 examples/company_user_roles_demo.py create mode 100644 examples/company_user_roles_demo_README.md create mode 100644 src/userverse_python_client/clients/company_user_roles_management.py create mode 100644 tests/test_company_user_roles_management_client.py diff --git a/examples/company_user_roles_demo.py b/examples/company_user_roles_demo.py new file mode 100644 index 0000000..04595e3 --- /dev/null +++ b/examples/company_user_roles_demo.py @@ -0,0 +1,275 @@ +"""Demo script for managing company roles via userverse_python_client.""" + +import argparse +import json +import os +import sys +from pathlib import Path +from typing import Any, Callable, Optional, Tuple + +# Allow running the example without installing the package first. +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) + +from userverse_models.user.user import UserLoginModel # noqa: E402 +from userverse_models.company.roles import ( # noqa: E402 + RoleCreateModel, + RoleUpdateModel, + RoleDeleteModel, + RoleQueryParamsModel, +) +from userverse_python_client import UverseUserClient # noqa: E402 +from userverse_python_client.clients.company_user_roles_management import ( # noqa: E402 + UverseCompanyUserRolesManagement, +) +from userverse_python_client.error_model import ClientErrorModel # noqa: E402 + +BASE_URL = "https://apps.oxillium-api.co.za/userverse" +USE_ENV_VALUE = object() + + +ClientProvider = Callable[[], Any] +ActionFunction = Callable[[Any], Any] + + +def require_env(name: str) -> str: + value = os.getenv(name) + if not value: + raise ValueError(f"Missing required env var: {name}") + return value + + +def require_int_env(name: str) -> int: + raw_value = require_env(name) + try: + return int(raw_value) + except ValueError as exc: + raise ValueError(f"{name} must be an integer, got '{raw_value}'") from exc + + +def optional_int_env(name: str) -> Optional[int]: + raw_value = os.getenv(name) + if raw_value is None or raw_value == "": + return None + try: + return int(raw_value) + except ValueError as exc: + raise ValueError(f"{name} must be an integer, got '{raw_value}'") from exc + + +def build_user_client() -> UverseUserClient: + return UverseUserClient(base_url=BASE_URL) + + +def build_role_client(token: str) -> UverseCompanyUserRolesManagement: + return UverseCompanyUserRolesManagement(base_url=BASE_URL, access_token=token) + + +def log_response(label: str, response: Any) -> None: + print(f"{label} Response:", response) + dump_method = getattr(response, "model_dump", None) + if callable(dump_method): + print(f"{label} Response (dict):") + print(json.dumps(dump_method(), indent=2, default=str)) + + +def login_user(client: UverseUserClient) -> str: + login_model = UserLoginModel( + email=require_env("USER_EMAIL"), + password=require_env("USER_PASSWORD"), + ) + response = client.user_login(login_model) + client.set_access_token(response.data.access_token) + log_response("Login", response) + return response.data.access_token + + +def require_company_id() -> int: + return require_int_env("COMPANY_ROLE_COMPANY_ID") + + +def build_role_query_params() -> RoleQueryParamsModel: + params: dict[str, object] = {} + + limit = optional_int_env("COMPANY_ROLE_QUERY_LIMIT") + if limit is not None: + params["limit"] = limit + + page = optional_int_env("COMPANY_ROLE_QUERY_PAGE") + if page is not None: + params["page"] = page + + for field, env_name in [ + ("name", "COMPANY_ROLE_QUERY_NAME"), + ("description", "COMPANY_ROLE_QUERY_DESCRIPTION"), + ]: + value = os.getenv(env_name) + if value: + params[field] = value + + return RoleQueryParamsModel(**params) + + +def list_roles(client: UverseCompanyUserRolesManagement) -> None: + company_id = require_company_id() + params = build_role_query_params() + response = client.get_company_roles(company_id=company_id, query_params=params) + log_response("List Roles", response) + + +def create_role(client: UverseCompanyUserRolesManagement) -> None: + company_id = require_company_id() + role = RoleCreateModel( + name=require_env("COMPANY_ROLE_CREATE_NAME"), + description=os.getenv("COMPANY_ROLE_CREATE_DESCRIPTION"), + ) + response = client.create_company_role(company_id=company_id, role_data=role) + log_response("Create Role", response) + + +def update_role(client: UverseCompanyUserRolesManagement) -> None: + company_id = require_company_id() + role_name = require_env("COMPANY_ROLE_UPDATE_ROLE_NAME") + role_update = RoleUpdateModel( + name=os.getenv("COMPANY_ROLE_UPDATE_NAME"), + description=os.getenv("COMPANY_ROLE_UPDATE_DESCRIPTION"), + ) + if not role_update.model_dump(exclude_none=True): + raise ValueError("Provide at least one COMPANY_ROLE_UPDATE_* value to update.") + response = client.update_company_role( + company_id=company_id, + role_name=role_name, + role_data=role_update, + ) + log_response("Update Role", response) + + +def delete_role(client: UverseCompanyUserRolesManagement) -> None: + company_id = require_company_id() + delete_model = RoleDeleteModel( + role_name_to_delete=require_env("COMPANY_ROLE_DELETE_NAME"), + replacement_role_name=require_env("COMPANY_ROLE_DELETE_REPLACEMENT"), + ) + response = client.delete_company_role( + company_id=company_id, + role_delete_data=delete_model, + ) + log_response("Delete Role", response) + + +def show_default_roles(_: UverseCompanyUserRolesManagement) -> None: + roles = UverseCompanyUserRolesManagement.list_default_roles() + print("Default Roles:", ", ".join(roles)) + + +def run_action( + action_name: str, + fn: ActionFunction, + client_provider: ClientProvider, +) -> Tuple[bool, Any]: + try: + client = client_provider() + result = fn(client) + return True, result + except ClientErrorModel as exc: + detail = exc.payload.detail + print(f"{action_name} failed ({exc.status_code}): {detail.message}") + print(f"Error details: {detail.error}") + except ValueError as exc: + print(f"{action_name} skipped: {exc}") + return False, None + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Company role management demo") + parser.add_argument("--login", action="store_true", help="Login user to get token") + parser.add_argument( + "--list-roles", + action="store_true", + help="List roles linked to COMPANY_ROLE_COMPANY_ID", + ) + parser.add_argument( + "--create-role", + action="store_true", + help="Create a new role for COMPANY_ROLE_COMPANY_ID", + ) + parser.add_argument( + "--update-role", + action="store_true", + help="Update a role for COMPANY_ROLE_COMPANY_ID", + ) + parser.add_argument( + "--delete-role", + action="store_true", + help="Delete a role from COMPANY_ROLE_COMPANY_ID", + ) + parser.add_argument( + "--default-roles", + action="store_true", + help="Print the built-in default role names", + ) + parser.add_argument( + "--all", + action="store_true", + help="Run login + list roles (requires env vars)", + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + user_client = build_user_client() + + if args.all: + args.login = True + args.list_roles = True + + access_token: Optional[str] = None + role_client: Optional[UverseCompanyUserRolesManagement] = None + logged_in = False + + def get_role_client() -> UverseCompanyUserRolesManagement: + nonlocal role_client, access_token, logged_in + if not logged_in or not access_token: + raise ValueError("Login is required before making company role calls.") + if role_client is None: + role_client = build_role_client(access_token) + return role_client + + actions = [ + ("Login", args.login, login_user, lambda: user_client, False), + ("List Roles", args.list_roles, list_roles, get_role_client, True), + ("Create Role", args.create_role, create_role, get_role_client, True), + ("Update Role", args.update_role, update_role, get_role_client, True), + ("Delete Role", args.delete_role, delete_role, get_role_client, True), + ("Default Roles", args.default_roles, show_default_roles, get_role_client, False), + ] + + if not any(flag for _, flag, _, _, _ in actions): + print("No actions selected. Try --help for available options.") + return + + for name, enabled, fn, client_provider, needs_token in actions: + if not enabled: + continue + if needs_token and not logged_in: + if args.login: + success, token = run_action("Login", login_user, lambda: user_client) + if not success: + continue + logged_in = True + access_token = token + role_client = None + else: + print(f"{name} skipped: missing --login for JWT-protected call") + continue + + success, result = run_action(name, fn, client_provider) + if name == "Login" and success: + logged_in = True + access_token = result + role_client = None + + +if __name__ == "__main__": # pragma: no cover + # run with uv: uv run -m examples.company_user_roles_demo --login --list-roles + main() diff --git a/examples/company_user_roles_demo_README.md b/examples/company_user_roles_demo_README.md new file mode 100644 index 0000000..d2a9b6b --- /dev/null +++ b/examples/company_user_roles_demo_README.md @@ -0,0 +1,118 @@ +Company user roles demo + +`examples/company_user_roles_demo.py` showcases how to manage custom company +roles via `UverseCompanyUserRolesManagement`. The script logs in a user first +and reuses that token for every role action. + +Running the demo + +Each action uses CLI flags and reads data from environment variables. If a +required env var is missing the script skips that action with a message, so you +can run only the flows you need. + +From the repo root: + +``` +uv run -m examples.company_user_roles_demo --help +``` + +Common usage + +Login and list roles: + +``` +USER_EMAIL="you@example.com" USER_PASSWORD="secret" \ +COMPANY_ROLE_COMPANY_ID=123 \ +uv run -m examples.company_user_roles_demo --login --list-roles +``` + +Create a role: + +``` +USER_EMAIL="you@example.com" USER_PASSWORD="secret" \ +COMPANY_ROLE_COMPANY_ID=123 \ +COMPANY_ROLE_CREATE_NAME="Supervisor" \ +COMPANY_ROLE_CREATE_DESCRIPTION="Can approve requests" \ +uv run -m examples.company_user_roles_demo --login --create-role +``` + +Update a role: + +``` +USER_EMAIL="you@example.com" USER_PASSWORD="secret" \ +COMPANY_ROLE_COMPANY_ID=123 \ +COMPANY_ROLE_UPDATE_ROLE_NAME="Supervisor" \ +COMPANY_ROLE_UPDATE_DESCRIPTION="Approves and audits" \ +uv run -m examples.company_user_roles_demo --login --update-role +``` + +Delete a role (requires a replacement role): + +``` +USER_EMAIL="you@example.com" USER_PASSWORD="secret" \ +COMPANY_ROLE_COMPANY_ID=123 \ +COMPANY_ROLE_DELETE_NAME="Seasonal Contractor" \ +COMPANY_ROLE_DELETE_REPLACEMENT="Viewer" \ +uv run -m examples.company_user_roles_demo --login --delete-role +``` + +Show default role names (no API call needed): + +``` +uv run -m examples.company_user_roles_demo --default-roles +``` + +Available flags + +- --login +- --list-roles +- --create-role +- --update-role +- --delete-role +- --default-roles +- --all (runs `--login --list-roles`) + +Client methods covered + +- user_login (fetches the JWT used by the role client) +- get_company_roles +- create_company_role +- update_company_role +- delete_company_role +- list_default_roles (local helper for built-in roles) + +Environment variables + +Login: +- USER_EMAIL +- USER_PASSWORD + +Shared: +- COMPANY_ROLE_COMPANY_ID (required for API actions) + +List roles (optional filters): +- COMPANY_ROLE_QUERY_LIMIT +- COMPANY_ROLE_QUERY_PAGE +- COMPANY_ROLE_QUERY_NAME +- COMPANY_ROLE_QUERY_DESCRIPTION + +Create role: +- COMPANY_ROLE_CREATE_NAME +- COMPANY_ROLE_CREATE_DESCRIPTION (optional) + +Update role: +- COMPANY_ROLE_UPDATE_ROLE_NAME +- COMPANY_ROLE_UPDATE_NAME (optional) +- COMPANY_ROLE_UPDATE_DESCRIPTION (optional) + +Delete role: +- COMPANY_ROLE_DELETE_NAME +- COMPANY_ROLE_DELETE_REPLACEMENT + +Notes + +- All role operations except `--default-roles` require logging in first. +- The role client is instantiated only after login so the Authorization header + always carries the fresh token. +- Server-side validation may reject special-use domains (e.g., `.test`, + `.example`); use realistic data for company roles to avoid failures. diff --git a/src/userverse_python_client/clients/company_user_roles_management.py b/src/userverse_python_client/clients/company_user_roles_management.py new file mode 100644 index 0000000..c421b6f --- /dev/null +++ b/src/userverse_python_client/clients/company_user_roles_management.py @@ -0,0 +1,112 @@ +from sverse_generic_models.generic_response import GenericResponseModel +from sverse_generic_models.generic_pagination import PaginatedResponse +from userverse_models.company.roles import ( + CompanyDefaultRoles, + RoleCreateModel, + RoleUpdateModel, + RoleDeleteModel, + RoleReadModel, + RoleQueryParamsModel, +) + +from ..http_client_base import BaseClient + + +class UverseCompanyUserRolesManagement(BaseClient): + def __init__(self, base_url, access_token, timeout=30): + super().__init__(base_url, access_token, timeout) + + @staticmethod + def list_default_roles() -> list[str]: + """Return the list of built-in company role names.""" + return [role.name_value for role in CompanyDefaultRoles] + + def get_company_roles( + self, + company_id: int, + query_params: RoleQueryParamsModel = RoleQueryParamsModel(), + ) -> GenericResponseModel[PaginatedResponse[RoleReadModel]]: + """Fetches a list of roles associated with the company.""" + params = query_params.model_dump(exclude_none=True) + + response = self._request("GET", f"/company/{company_id}/roles", params=params) + + if not response or "data" not in response: + raise ValueError("Invalid response from get company roles endpoint") + + data = response.get("data", {}) + if not isinstance(data, dict): + raise ValueError( + f"Expected role pagination data to be a dict, got {type(data)}" + ) + + return GenericResponseModel[PaginatedResponse[RoleReadModel]].model_validate( + response + ) + + def create_company_role( + self, + company_id: int, + role_data: RoleCreateModel, + ) -> GenericResponseModel[RoleReadModel]: + """Creates a new role for the specified company.""" + payload = role_data.model_dump(exclude_none=True) + response = self._request( + "POST", + f"/company/{company_id}/roles", + json=payload, + ) + + if not response or "data" not in response: + raise ValueError("Invalid response from create company role endpoint") + + data = response.get("data", {}) + if not isinstance(data, dict): + raise ValueError(f"Expected role data to be a dict, got {type(data)}") + + return GenericResponseModel[RoleReadModel].model_validate(response) + + def update_company_role( + self, + company_id: int, + role_name: str, + role_data: RoleUpdateModel, + ) -> GenericResponseModel[RoleReadModel]: + """Updates an existing role for the specified company.""" + payload = role_data.model_dump(exclude_none=True) + response = self._request( + "PUT", + f"/company/{company_id}/roles/{role_name}", + json=payload, + ) + + if not response or "data" not in response: + raise ValueError("Invalid response from update company role endpoint") + + data = response.get("data", {}) + if not isinstance(data, dict): + raise ValueError(f"Expected role data to be a dict, got {type(data)}") + + return GenericResponseModel[RoleReadModel].model_validate(response) + + def delete_company_role( + self, + company_id: int, + role_delete_data: RoleDeleteModel, + ) -> GenericResponseModel[None]: + """Deletes a role from the specified company, replacing it with another role.""" + payload = role_delete_data.model_dump(exclude_none=True) + response = self._request( + "DELETE", + f"/company/{company_id}/roles", + json=payload, + ) + + if not response or "data" not in response: + raise ValueError("Invalid response from delete company role endpoint") + + data = response.get("data", {}) + if not isinstance(data, dict): + raise ValueError(f"Expected response data to be a dict, got {type(data)}") + + return GenericResponseModel[None].model_validate(response) diff --git a/tests/conftest.py b/tests/conftest.py index f735faf..31fcfef 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,7 @@ import sys from dataclasses import dataclass +from enum import Enum from types import ModuleType from typing import Any @@ -127,6 +128,47 @@ class _CompanyUserReadModel: role_name: str | None = None +@dataclass +class _RoleCreateModel(_ModelDumpMixin): + name: str | None = None + description: str | None = None + + +@dataclass +class _RoleUpdateModel(_ModelDumpMixin): + name: str | None = None + description: str | None = None + + +@dataclass +class _RoleDeleteModel(_ModelDumpMixin): + replacement_role_name: str | None = None + role_name_to_delete: str | None = None + + +@dataclass +class _RoleReadModel: + name: str | None = None + description: str | None = None + + +@dataclass +class _RoleQueryParamsModel(_ModelDumpMixin): + limit: int | None = None + page: int | None = None + name: str | None = None + description: str | None = None + + +class _CompanyDefaultRoles(str, Enum): + ADMINISTRATOR = "Administrator: Full access" + VIEWER = "Viewer: Read-only access" + + @property + def name_value(self) -> str: + return self.value.split(":")[0].strip() + + def _ensure_module(name: str) -> ModuleType: module = sys.modules.get(name) if module is None: @@ -168,3 +210,11 @@ def _ensure_module(name: str) -> ModuleType: company_user_module.CompanyUserAddModel = _CompanyUserAddModel company_user_module.CompanyUserReadModel = _CompanyUserReadModel company_module.user = company_user_module +company_roles_module = _ensure_module("userverse_models.company.roles") +company_roles_module.CompanyDefaultRoles = _CompanyDefaultRoles +company_roles_module.RoleCreateModel = _RoleCreateModel +company_roles_module.RoleUpdateModel = _RoleUpdateModel +company_roles_module.RoleDeleteModel = _RoleDeleteModel +company_roles_module.RoleReadModel = _RoleReadModel +company_roles_module.RoleQueryParamsModel = _RoleQueryParamsModel +company_module.roles = company_roles_module diff --git a/tests/test_company_user_roles_management_client.py b/tests/test_company_user_roles_management_client.py new file mode 100644 index 0000000..134187c --- /dev/null +++ b/tests/test_company_user_roles_management_client.py @@ -0,0 +1,160 @@ +import pytest + +from userverse_python_client.clients.company_user_roles_management import ( + UverseCompanyUserRolesManagement, +) +from userverse_models.company.roles import ( + RoleCreateModel, + RoleUpdateModel, + RoleDeleteModel, + RoleQueryParamsModel, + CompanyDefaultRoles, +) + + +def _client() -> UverseCompanyUserRolesManagement: + return UverseCompanyUserRolesManagement("https://example.test", access_token="tok") + + +def test_get_company_roles_calls_endpoint() -> None: + client = _client() + params = RoleQueryParamsModel(limit=5, name="Viewer") + called: dict[str, object] = {} + + def fake_request(method, path, params=None, **_kwargs): + called.update(method=method, path=path, params=params) + return { + "data": { + "records": [{"name": "Viewer"}], + "pagination": {"limit": 5, "current_page": 1}, + } + } + + client._request = fake_request # type: ignore[method-assign] + response = client.get_company_roles(company_id=3, query_params=params) + + assert called == { + "method": "GET", + "path": "/company/3/roles", + "params": params.model_dump(exclude_none=True), + } + assert response["data"]["records"][0]["name"] == "Viewer" + + +def test_get_company_roles_requires_dict_payload() -> None: + client = _client() + params = RoleQueryParamsModel() + + def fake_request(*_args, **_kwargs): + return {"data": []} + + client._request = fake_request # type: ignore[method-assign] + with pytest.raises(ValueError, match="role pagination data"): + client.get_company_roles(1, params) + + +def test_create_company_role_posts_payload() -> None: + client = _client() + role = RoleCreateModel(name="Editor", description="Edit access") + called: dict[str, object] = {} + + def fake_request(method, path, json=None, **_kwargs): + called.update(method=method, path=path, json=json) + return {"data": {"name": "Editor"}} + + client._request = fake_request # type: ignore[method-assign] + response = client.create_company_role(2, role) + + assert called == { + "method": "POST", + "path": "/company/2/roles", + "json": role.model_dump(exclude_none=True), + } + assert response["data"]["name"] == "Editor" + + +def test_create_company_role_requires_dict_payload() -> None: + client = _client() + role = RoleCreateModel(name="Editor") + + def fake_request(*_args, **_kwargs): + return {"data": []} + + client._request = fake_request # type: ignore[method-assign] + with pytest.raises(ValueError, match="Expected role data"): + client.create_company_role(2, role) + + +def test_update_company_role_puts_payload() -> None: + client = _client() + role = RoleUpdateModel(description="Read-only") + called: dict[str, object] = {} + + def fake_request(method, path, json=None, **_kwargs): + called.update(method=method, path=path, json=json) + return {"data": {"name": "Viewer", "description": "Read-only"}} + + client._request = fake_request # type: ignore[method-assign] + response = client.update_company_role(5, "Viewer", role) + + assert called == { + "method": "PUT", + "path": "/company/5/roles/Viewer", + "json": role.model_dump(exclude_none=True), + } + assert response["data"]["description"] == "Read-only" + + +def test_update_company_role_requires_dict_payload() -> None: + client = _client() + role = RoleUpdateModel(description="Nope") + + def fake_request(*_args, **_kwargs): + return {"data": []} + + client._request = fake_request # type: ignore[method-assign] + with pytest.raises(ValueError, match="Expected role data"): + client.update_company_role(1, "Viewer", role) + + +def test_delete_company_role_sends_payload() -> None: + client = _client() + delete_model = RoleDeleteModel( + replacement_role_name="Viewer", role_name_to_delete="Editor" + ) + called: dict[str, object] = {} + + def fake_request(method, path, json=None, **_kwargs): + called.update(method=method, path=path, json=json) + return {"data": {"status": "ok"}} + + client._request = fake_request # type: ignore[method-assign] + response = client.delete_company_role(8, delete_model) + + assert called == { + "method": "DELETE", + "path": "/company/8/roles", + "json": delete_model.model_dump(exclude_none=True), + } + assert response["data"]["status"] == "ok" + + +def test_delete_company_role_requires_dict_payload() -> None: + client = _client() + delete_model = RoleDeleteModel( + replacement_role_name="Viewer", role_name_to_delete="Editor" + ) + + def fake_request(*_args, **_kwargs): + return {"data": []} + + client._request = fake_request # type: ignore[method-assign] + with pytest.raises(ValueError, match="Expected response data"): + client.delete_company_role(1, delete_model) + + +def test_list_default_roles_returns_names() -> None: + roles = UverseCompanyUserRolesManagement.list_default_roles() + + assert "Administrator" in roles + assert all(isinstance(role, str) for role in roles) From 02cec603b7bc8778e9b762f11b8a98804e975db7 Mon Sep 17 00:00:00 2001 From: "Skhendle@gmail.com" Date: Fri, 30 Jan 2026 15:39:15 +0200 Subject: [PATCH 2/3] black formatting --- examples/company_user_roles_demo.py | 8 +++++++- src/userverse_python_client/__init__.py | 6 +++++- .../clients/company_user_roles_management.py | 6 +++--- tests/test_company_user_management_client.py | 4 +++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/examples/company_user_roles_demo.py b/examples/company_user_roles_demo.py index 04595e3..b51c429 100644 --- a/examples/company_user_roles_demo.py +++ b/examples/company_user_roles_demo.py @@ -241,7 +241,13 @@ def get_role_client() -> UverseCompanyUserRolesManagement: ("Create Role", args.create_role, create_role, get_role_client, True), ("Update Role", args.update_role, update_role, get_role_client, True), ("Delete Role", args.delete_role, delete_role, get_role_client, True), - ("Default Roles", args.default_roles, show_default_roles, get_role_client, False), + ( + "Default Roles", + args.default_roles, + show_default_roles, + get_role_client, + False, + ), ] if not any(flag for _, flag, _, _, _ in actions): diff --git a/src/userverse_python_client/__init__.py b/src/userverse_python_client/__init__.py index ba0ee30..d41d022 100644 --- a/src/userverse_python_client/__init__.py +++ b/src/userverse_python_client/__init__.py @@ -6,4 +6,8 @@ from .clients.company import UverseCompanyClient from .clients.company_user_management import UverseCompanyUserManagementClient -__all__ = ["UverseUserClient", "UverseCompanyClient", "UverseCompanyUserManagementClient"] \ No newline at end of file +__all__ = [ + "UverseUserClient", + "UverseCompanyClient", + "UverseCompanyUserManagementClient", +] diff --git a/src/userverse_python_client/clients/company_user_roles_management.py b/src/userverse_python_client/clients/company_user_roles_management.py index c421b6f..a70fbf1 100644 --- a/src/userverse_python_client/clients/company_user_roles_management.py +++ b/src/userverse_python_client/clients/company_user_roles_management.py @@ -43,7 +43,7 @@ def get_company_roles( return GenericResponseModel[PaginatedResponse[RoleReadModel]].model_validate( response ) - + def create_company_role( self, company_id: int, @@ -65,7 +65,7 @@ def create_company_role( raise ValueError(f"Expected role data to be a dict, got {type(data)}") return GenericResponseModel[RoleReadModel].model_validate(response) - + def update_company_role( self, company_id: int, @@ -88,7 +88,7 @@ def update_company_role( raise ValueError(f"Expected role data to be a dict, got {type(data)}") return GenericResponseModel[RoleReadModel].model_validate(response) - + def delete_company_role( self, company_id: int, diff --git a/tests/test_company_user_management_client.py b/tests/test_company_user_management_client.py index e3c096c..9f0a29e 100644 --- a/tests/test_company_user_management_client.py +++ b/tests/test_company_user_management_client.py @@ -8,7 +8,9 @@ def _client() -> UverseCompanyUserManagementClient: - return UverseCompanyUserManagementClient("https://example.test", access_token="token") + return UverseCompanyUserManagementClient( + "https://example.test", access_token="token" + ) def test_add_user_to_company_posts_payload() -> None: From c9877c70929109d6f7ef13b306fe2abad1258304 Mon Sep 17 00:00:00 2001 From: "Skhendle@gmail.com" Date: Fri, 30 Jan 2026 15:42:56 +0200 Subject: [PATCH 3/3] expose UverseCompanyUserRolesManagement --- src/userverse_python_client/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/userverse_python_client/__init__.py b/src/userverse_python_client/__init__.py index d41d022..e975f65 100644 --- a/src/userverse_python_client/__init__.py +++ b/src/userverse_python_client/__init__.py @@ -1,13 +1,13 @@ """userverse_python_client public API.""" -from typing import TYPE_CHECKING, Any - from .clients.user import UverseUserClient from .clients.company import UverseCompanyClient from .clients.company_user_management import UverseCompanyUserManagementClient +from .clients.company_user_roles_management import UverseCompanyUserRolesManagement __all__ = [ "UverseUserClient", "UverseCompanyClient", "UverseCompanyUserManagementClient", + "UverseCompanyUserRolesManagement", ]