diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 55203dc..39dc37f 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -51,7 +51,7 @@ jobs: run: export PYTHONPATH=$PWD && uv run --extra dev pytest --junitxml=pytest.xml --cov=perses_api --cov-report xml tests/unit/ | tee pytest-coverage.txt - name: Execute the coverage checks - uses: MishaKav/pytest-coverage-comment@v1.10.0 + uses: MishaKav/pytest-coverage-comment@v1.11.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} pytest-coverage-path: ./pytest-coverage.txt diff --git a/README.md b/README.md index 22bbd64..fb0bd8f 100644 --- a/README.md +++ b/README.md @@ -83,10 +83,10 @@ client = APIModel(host="http://localhost:8080", username="admin", password="secr client = APIModel( host="http://localhost:8080", token="", - timeout=30.0, # request timeout in seconds (default: 10) - http2_support=False, # enable HTTP/2 (requires httpx[http2]) - num_pools=10, # max concurrent connections - retries=False, # retry failed requests + timeout=30.0, # request timeout in seconds (default: 10) + http2_support=False, # enable HTTP/2 (requires httpx[http2]) + num_pools=10, # max concurrent connections + retries=False, # retry failed requests follow_redirects=True, # follow HTTP redirects ) ``` diff --git a/docs/coverage.svg b/docs/coverage.svg index 33bbdde..fbe15c3 100644 --- a/docs/coverage.svg +++ b/docs/coverage.svg @@ -1 +1 @@ -coverage: 89.12%coverage89.12% \ No newline at end of file +coverage: 89.27%coverage89.27% \ No newline at end of file diff --git a/perses_api/__init__.py b/perses_api/__init__.py index 854ad8c..99a3e0b 100644 --- a/perses_api/__init__.py +++ b/perses_api/__init__.py @@ -1,36 +1,36 @@ -from .model import APIModel from .api import Api -from .project import Project from .dashboard import Dashboard +from .datasource import GlobalDatasource, ProjectDatasource from .ephemeral_dashboard import EphemeralDashboard -from .datasource import ProjectDatasource, GlobalDatasource -from .variable import ProjectVariable, GlobalVariable -from .role import ProjectRole, GlobalRole -from .role_binding import ProjectRoleBinding, GlobalRoleBinding -from .secret import ProjectSecret, GlobalSecret -from .user import User -from .plugin import Plugin from .migrate import Migrate +from .model import APIModel +from .plugin import Plugin +from .project import Project +from .role import GlobalRole, ProjectRole +from .role_binding import GlobalRoleBinding, ProjectRoleBinding +from .secret import GlobalSecret, ProjectSecret +from .user import User from .validate import Validate +from .variable import GlobalVariable, ProjectVariable __all__ = [ "APIModel", "Api", - "Project", "Dashboard", "EphemeralDashboard", - "ProjectDatasource", "GlobalDatasource", - "ProjectVariable", + "GlobalRole", + "GlobalRoleBinding", + "GlobalSecret", "GlobalVariable", + "Migrate", + "Plugin", + "Project", + "ProjectDatasource", "ProjectRole", - "GlobalRole", "ProjectRoleBinding", - "GlobalRoleBinding", "ProjectSecret", - "GlobalSecret", + "ProjectVariable", "User", - "Plugin", - "Migrate", "Validate", ] diff --git a/perses_api/_base.py b/perses_api/_base.py index ffeb2a8..2430a90 100644 --- a/perses_api/_base.py +++ b/perses_api/_base.py @@ -3,6 +3,8 @@ import logging from .api import Api + +logger = logging.getLogger(__name__) from .model import APIModel, RequestsMethods @@ -30,7 +32,7 @@ def _base_path(self) -> str: """ raise NotImplementedError - def _get_all(self, name: str = None) -> list: + def _get_all(self, name: str | None = None) -> list: """The method includes a functionality to retrieve all resources, optionally filtered by name Args: @@ -47,8 +49,8 @@ def _get_all(self, name: str = None) -> list: path = f"{path}?name={name}" result = self.api.call_the_api(path) if not isinstance(result, list): - logging.error(f"Failed to retrieve resources from {self._base_path()}.") - raise Exception(result) + logger.error(f"Failed to retrieve resources from {self._base_path()}.") + raise TypeError(result) return result def _get_one(self, name: str) -> dict: @@ -68,8 +70,8 @@ def _get_one(self, name: str) -> dict: raise ValueError("name must not be empty") result = self.api.call_the_api(f"{self._base_path()}/{name}") if not isinstance(result, dict): - logging.error(f"Failed to retrieve resource: {name}") - raise Exception(result) + logger.error(f"Failed to retrieve resource: {name}") + raise TypeError(result) return result def _create(self, body) -> dict: @@ -90,8 +92,8 @@ def _create(self, body) -> dict: json_complete=body.model_dump_json(by_alias=True, exclude_none=True), ) if not isinstance(result, dict): - logging.error(f"Failed to create resource at {self._base_path()}.") - raise Exception(result) + logger.error(f"Failed to create resource at {self._base_path()}.") + raise TypeError(result) return result def _update(self, name: str, body) -> dict: @@ -116,8 +118,8 @@ def _update(self, name: str, body) -> dict: json_complete=body.model_dump_json(by_alias=True, exclude_none=True), ) if not isinstance(result, dict): - logging.error(f"Failed to update resource: {name}") - raise Exception(result) + logger.error(f"Failed to update resource: {name}") + raise TypeError(result) return result def _delete(self, name: str) -> None: diff --git a/perses_api/api.py b/perses_api/api.py index e428706..e0256fb 100644 --- a/perses_api/api.py +++ b/perses_api/api.py @@ -4,12 +4,14 @@ import base64 import json import logging -from typing import Any, Union +from typing import Any import httpx from .model import APIModel, RequestsMethods +logger = logging.getLogger(__name__) + class Api: """The class includes all necessary methods to access the Perses API @@ -28,7 +30,7 @@ def call_the_api( self, api_call: str, method: RequestsMethods = RequestsMethods.GET, - json_complete: str = None, + json_complete: str | None = None, response_status_code: bool = False, ) -> Any: """The method includes a functionality to execute a defined API call against the Perses endpoints @@ -77,8 +79,8 @@ async def _run(): return self._check_the_api_call_response(response, response_status_code) def create_the_http_api_client( - self, headers: dict = None - ) -> Union[httpx.Client, httpx.AsyncClient]: + self, headers: dict | None = None + ) -> httpx.Client | httpx.AsyncClient: """The method includes a functionality to create the HTTP client based on the API model configuration Args: @@ -119,7 +121,7 @@ def create_the_http_api_client( def _send_request( self, - http: Union[httpx.Client, httpx.AsyncClient], + http: httpx.Client | httpx.AsyncClient, method: RequestsMethods, api_url: str, json_complete: str, @@ -141,7 +143,7 @@ def _send_request( if method in (RequestsMethods.GET, RequestsMethods.DELETE): return http.request(method.value, api_url) if json_complete is None: - logging.error("Please define the json_complete.") + logger.error("Please define the json_complete.") raise ValueError(f"json_complete is required for {method.value}") return http.request(method.value, api_url, content=json_complete) diff --git a/perses_api/dashboard.py b/perses_api/dashboard.py index c5de14f..1ec8622 100644 --- a/perses_api/dashboard.py +++ b/perses_api/dashboard.py @@ -3,7 +3,9 @@ import logging from .api import Api -from .model import APIModel, APIEndpoints, RequestsMethods + +logger = logging.getLogger(__name__) +from .model import APIEndpoints, APIModel, RequestsMethods from .model import Dashboard as DashboardModel @@ -20,7 +22,7 @@ class Dashboard: def __init__(self, perses_api_model: APIModel): self.api = Api(perses_api_model) - def get_dashboards(self, project_name: str, name: str = None) -> list: + def get_dashboards(self, project_name: str, name: str | None = None) -> list: """The method includes a functionality to retrieve all dashboards within a project Args: @@ -41,8 +43,8 @@ def get_dashboards(self, project_name: str, name: str = None) -> list: path = f"{path}?name={name}" result = self.api.call_the_api(path) if not isinstance(result, list): - logging.error("Failed to retrieve dashboards.") - raise Exception(result) + logger.error("Failed to retrieve dashboards.") + raise TypeError(result) return result def get_dashboard(self, project_name: str, name: str) -> dict: @@ -67,8 +69,8 @@ def get_dashboard(self, project_name: str, name: str) -> dict: APIEndpoints.DASHBOARD.value.format(project=project_name, name=name) ) if not isinstance(result, dict): - logging.error(f"Failed to retrieve dashboard: {name}") - raise Exception(result) + logger.error(f"Failed to retrieve dashboard: {name}") + raise TypeError(result) return result def create_dashboard(self, project_name: str, dashboard: DashboardModel) -> dict: @@ -93,8 +95,8 @@ def create_dashboard(self, project_name: str, dashboard: DashboardModel) -> dict json_complete=dashboard.model_dump_json(by_alias=True, exclude_none=True), ) if not isinstance(result, dict): - logging.error("Failed to create dashboard.") - raise Exception(result) + logger.error("Failed to create dashboard.") + raise TypeError(result) return result def update_dashboard( @@ -124,8 +126,8 @@ def update_dashboard( json_complete=dashboard.model_dump_json(by_alias=True, exclude_none=True), ) if not isinstance(result, dict): - logging.error(f"Failed to update dashboard: {name}") - raise Exception(result) + logger.error(f"Failed to update dashboard: {name}") + raise TypeError(result) return result def delete_dashboard(self, project_name: str, name: str) -> None: diff --git a/perses_api/datasource.py b/perses_api/datasource.py index ea9154e..d4b8e8e 100644 --- a/perses_api/datasource.py +++ b/perses_api/datasource.py @@ -1,7 +1,7 @@ from __future__ import annotations from ._base import ResourceBase -from .model import APIModel, APIEndpoints +from .model import APIEndpoints, APIModel class DatasourceBase(ResourceBase): @@ -14,7 +14,7 @@ class DatasourceBase(ResourceBase): api (Api): This is where we store the api """ - def get_datasources(self, name: str = None) -> list: + def get_datasources(self, name: str | None = None) -> list: """The method includes a functionality to retrieve all datasources Args: diff --git a/perses_api/ephemeral_dashboard.py b/perses_api/ephemeral_dashboard.py index ee61a6d..ac80ca2 100644 --- a/perses_api/ephemeral_dashboard.py +++ b/perses_api/ephemeral_dashboard.py @@ -3,7 +3,9 @@ import logging from .api import Api -from .model import APIModel, APIEndpoints, RequestsMethods + +logger = logging.getLogger(__name__) +from .model import APIEndpoints, APIModel, RequestsMethods from .model import EphemeralDashboard as EphemeralDashboardModel @@ -20,7 +22,9 @@ class EphemeralDashboard: def __init__(self, perses_api_model: APIModel): self.api = Api(perses_api_model) - def get_ephemeral_dashboards(self, project_name: str, name: str = None) -> list: + def get_ephemeral_dashboards( + self, project_name: str, name: str | None = None + ) -> list: """The method includes a functionality to retrieve all ephemeral dashboards within a project Args: @@ -41,8 +45,8 @@ def get_ephemeral_dashboards(self, project_name: str, name: str = None) -> list: path = f"{path}?name={name}" result = self.api.call_the_api(path) if not isinstance(result, list): - logging.error("Failed to retrieve ephemeral dashboards.") - raise Exception(result) + logger.error("Failed to retrieve ephemeral dashboards.") + raise TypeError(result) return result def get_ephemeral_dashboard(self, project_name: str, name: str) -> dict: @@ -69,8 +73,8 @@ def get_ephemeral_dashboard(self, project_name: str, name: str) -> dict: ) ) if not isinstance(result, dict): - logging.error(f"Failed to retrieve ephemeral dashboard: {name}") - raise Exception(result) + logger.error(f"Failed to retrieve ephemeral dashboard: {name}") + raise TypeError(result) return result def create_ephemeral_dashboard( @@ -99,8 +103,8 @@ def create_ephemeral_dashboard( ), ) if not isinstance(result, dict): - logging.error("Failed to create ephemeral dashboard.") - raise Exception(result) + logger.error("Failed to create ephemeral dashboard.") + raise TypeError(result) return result def update_ephemeral_dashboard( @@ -134,8 +138,8 @@ def update_ephemeral_dashboard( ), ) if not isinstance(result, dict): - logging.error(f"Failed to update ephemeral dashboard: {name}") - raise Exception(result) + logger.error(f"Failed to update ephemeral dashboard: {name}") + raise TypeError(result) return result def delete_ephemeral_dashboard(self, project_name: str, name: str) -> None: diff --git a/perses_api/migrate.py b/perses_api/migrate.py index 12e63ec..dd7f69c 100644 --- a/perses_api/migrate.py +++ b/perses_api/migrate.py @@ -4,7 +4,9 @@ import logging from .api import Api -from .model import APIModel, APIEndpoints, RequestsMethods + +logger = logging.getLogger(__name__) +from .model import APIEndpoints, APIModel, RequestsMethods class Migrate: @@ -20,7 +22,9 @@ class Migrate: def __init__(self, perses_api_model: APIModel): self.api = Api(perses_api_model) - def migrate(self, grafana_dashboard: dict, migration_input: dict = None) -> dict: + def migrate( + self, grafana_dashboard: dict, migration_input: dict | None = None + ) -> dict: """The method includes a functionality to migrate a Grafana dashboard to the Perses format Args: @@ -45,10 +49,10 @@ def migrate(self, grafana_dashboard: dict, migration_input: dict = None) -> dict json_complete=json.dumps(body), ) if not isinstance(result, dict): - logging.error("Migration failed.") - raise Exception(result) + logger.error("Migration failed.") + raise TypeError(result) if "kind" not in result: error_msg = result.get("message", "Unknown error") - logging.error(f"Migration failed: {error_msg}") - raise Exception(result) + logger.error(f"Migration failed: {error_msg}") + raise TypeError(result) return result diff --git a/perses_api/model.py b/perses_api/model.py index 0feebaf..b7b1318 100644 --- a/perses_api/model.py +++ b/perses_api/model.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from enum import Enum -from typing import Any, Optional +from typing import Any from pydantic import BaseModel, ConfigDict, Field from pydantic.alias_generators import to_camel @@ -41,13 +41,11 @@ class APIModel: """ host: str #: The host of the Perses instance - token: Optional[str] = None #: Bearer token for authentication - username: Optional[str] = None #: Username for basic authentication - password: Optional[str] = None #: Password for basic authentication + token: str | None = None #: Bearer token for authentication + username: str | None = None #: Username for basic authentication + password: str | None = None #: Password for basic authentication timeout: float = 10.0 #: Timeout of the API call in seconds - headers: Optional[dict] = ( - None #: Additional HTTP headers to include in every request - ) + headers: dict | None = None #: Additional HTTP headers to include in every request http2_support: bool = False #: Whether to use HTTP/2 ssl_context: Any = None #: Custom SSL context or certificate path num_pools: int = 10 #: Maximum number of HTTP connections @@ -122,10 +120,10 @@ class Metadata(_CamelModel): """ name: str - project: Optional[str] = None - created_at: Optional[str] = None - updated_at: Optional[str] = None - version: Optional[int] = None + project: str | None = None + created_at: str | None = None + updated_at: str | None = None + version: int | None = None # --------------------------------------------------------------------------- @@ -140,7 +138,7 @@ class ProjectSpec(_CamelModel): display (dict): Specify optional display properties such as a human-readable name (default None) """ - display: Optional[dict] = None + display: dict | None = None class Project(_CamelModel): @@ -170,13 +168,13 @@ class DashboardSpec(_CamelModel): refresh_interval (str): Specify the default auto-refresh interval, e.g. 30s (default None) """ - display: Optional[dict] = None - datasources: Optional[dict] = None - variables: Optional[list] = None - panels: Optional[dict] = None - layouts: Optional[list] = None - duration: Optional[str] = None - refresh_interval: Optional[str] = None + display: dict | None = None + datasources: dict | None = None + variables: list | None = None + panels: dict | None = None + layouts: list | None = None + duration: str | None = None + refresh_interval: str | None = None class Dashboard(_CamelModel): @@ -208,13 +206,13 @@ class EphemeralDashboardSpec(_CamelModel): """ ttl: str - display: Optional[dict] = None - datasources: Optional[dict] = None - variables: Optional[list] = None - panels: Optional[dict] = None - layouts: Optional[list] = None - duration: Optional[str] = None - refresh_interval: Optional[str] = None + display: dict | None = None + datasources: dict | None = None + variables: list | None = None + panels: dict | None = None + layouts: list | None = None + duration: str | None = None + refresh_interval: str | None = None class EphemeralDashboard(_CamelModel): @@ -463,10 +461,10 @@ class UserSpec(_CamelModel): oauth_providers (list): Specify the list of OAuth provider configurations (default None) """ - first_name: Optional[str] = None - last_name: Optional[str] = None - native_provider: Optional[dict] = None - oauth_providers: Optional[list] = None + first_name: str | None = None + last_name: str | None = None + native_provider: dict | None = None + oauth_providers: list | None = None class User(_CamelModel): @@ -504,7 +502,7 @@ class PluginEntry(_CamelModel): """ kind: str - display: Optional[dict] = None + display: dict | None = None class PluginModuleSpec(_CamelModel): @@ -515,7 +513,7 @@ class PluginModuleSpec(_CamelModel): plugins (list[PluginEntry]): Specify the list of plugins provided by this module (default []) """ - schemas_path: Optional[str] = None + schemas_path: str | None = None plugins: list[PluginEntry] = Field(default_factory=list) diff --git a/perses_api/plugin.py b/perses_api/plugin.py index efe4fe6..087c33e 100644 --- a/perses_api/plugin.py +++ b/perses_api/plugin.py @@ -3,7 +3,9 @@ import logging from .api import Api -from .model import APIModel, APIEndpoints + +logger = logging.getLogger(__name__) +from .model import APIEndpoints, APIModel class Plugin: @@ -30,6 +32,6 @@ def get_plugins(self) -> list: """ result = self.api.call_the_api(APIEndpoints.PLUGINS.value) if not isinstance(result, list): - logging.error("Failed to retrieve plugins.") - raise Exception(result) + logger.error("Failed to retrieve plugins.") + raise TypeError(result) return result diff --git a/perses_api/project.py b/perses_api/project.py index 8a1aeb6..4292cf3 100644 --- a/perses_api/project.py +++ b/perses_api/project.py @@ -3,7 +3,9 @@ import logging from .api import Api -from .model import APIModel, APIEndpoints, RequestsMethods + +logger = logging.getLogger(__name__) +from .model import APIEndpoints, APIModel, RequestsMethods from .model import Project as ProjectModel @@ -20,7 +22,7 @@ class Project: def __init__(self, perses_api_model: APIModel): self.api = Api(perses_api_model) - def get_projects(self, name: str = None) -> list: + def get_projects(self, name: str | None = None) -> list: """The method includes a functionality to retrieve all projects Args: @@ -37,8 +39,8 @@ def get_projects(self, name: str = None) -> list: path = f"{path}?name={name}" result = self.api.call_the_api(path) if not isinstance(result, list): - logging.error("Failed to retrieve projects.") - raise Exception(result) + logger.error("Failed to retrieve projects.") + raise TypeError(result) return result def get_project(self, name: str) -> dict: @@ -58,8 +60,8 @@ def get_project(self, name: str) -> dict: raise ValueError("name must not be empty") result = self.api.call_the_api(APIEndpoints.PROJECT.value.format(project=name)) if not isinstance(result, dict): - logging.error(f"Failed to retrieve project: {name}") - raise Exception(result) + logger.error(f"Failed to retrieve project: {name}") + raise TypeError(result) return result def create_project(self, project: ProjectModel) -> dict: @@ -80,8 +82,8 @@ def create_project(self, project: ProjectModel) -> dict: json_complete=project.model_dump_json(by_alias=True, exclude_none=True), ) if not isinstance(result, dict): - logging.error("Failed to create project.") - raise Exception(result) + logger.error("Failed to create project.") + raise TypeError(result) return result def update_project(self, name: str, project: ProjectModel) -> dict: @@ -106,8 +108,8 @@ def update_project(self, name: str, project: ProjectModel) -> dict: json_complete=project.model_dump_json(by_alias=True, exclude_none=True), ) if not isinstance(result, dict): - logging.error(f"Failed to update project: {name}") - raise Exception(result) + logger.error(f"Failed to update project: {name}") + raise TypeError(result) return result def delete_project(self, name: str) -> None: diff --git a/perses_api/role.py b/perses_api/role.py index e57a2ab..b51f089 100644 --- a/perses_api/role.py +++ b/perses_api/role.py @@ -1,7 +1,7 @@ from __future__ import annotations from ._base import ResourceBase -from .model import APIModel, APIEndpoints +from .model import APIEndpoints, APIModel class RoleBase(ResourceBase): @@ -14,7 +14,7 @@ class RoleBase(ResourceBase): api (Api): This is where we store the api """ - def get_roles(self, name: str = None) -> list: + def get_roles(self, name: str | None = None) -> list: """The method includes a functionality to retrieve all roles Args: diff --git a/perses_api/role_binding.py b/perses_api/role_binding.py index 26a6d53..fc30250 100644 --- a/perses_api/role_binding.py +++ b/perses_api/role_binding.py @@ -1,7 +1,7 @@ from __future__ import annotations from ._base import ResourceBase -from .model import APIModel, APIEndpoints +from .model import APIEndpoints, APIModel class RoleBindingBase(ResourceBase): @@ -14,7 +14,7 @@ class RoleBindingBase(ResourceBase): api (Api): This is where we store the api """ - def get_role_bindings(self, name: str = None) -> list: + def get_role_bindings(self, name: str | None = None) -> list: """The method includes a functionality to retrieve all role bindings Args: diff --git a/perses_api/secret.py b/perses_api/secret.py index 6a3285f..cbdbbb5 100644 --- a/perses_api/secret.py +++ b/perses_api/secret.py @@ -1,7 +1,7 @@ from __future__ import annotations from ._base import ResourceBase -from .model import APIModel, APIEndpoints +from .model import APIEndpoints, APIModel class SecretBase(ResourceBase): @@ -14,7 +14,7 @@ class SecretBase(ResourceBase): api (Api): This is where we store the api """ - def get_secrets(self, name: str = None) -> list: + def get_secrets(self, name: str | None = None) -> list: """The method includes a functionality to retrieve all secrets Args: diff --git a/perses_api/user.py b/perses_api/user.py index af2fe0f..cf2e10d 100644 --- a/perses_api/user.py +++ b/perses_api/user.py @@ -3,7 +3,9 @@ import logging from .api import Api -from .model import APIModel, APIEndpoints, RequestsMethods + +logger = logging.getLogger(__name__) +from .model import APIEndpoints, APIModel, RequestsMethods from .model import User as UserModel @@ -20,7 +22,7 @@ class User: def __init__(self, perses_api_model: APIModel): self.api = Api(perses_api_model) - def get_users(self, name: str = None) -> list: + def get_users(self, name: str | None = None) -> list: """The method includes a functionality to retrieve all users Args: @@ -37,8 +39,8 @@ def get_users(self, name: str = None) -> list: path = f"{path}?name={name}" result = self.api.call_the_api(path) if not isinstance(result, list): - logging.error("Failed to retrieve users.") - raise Exception(result) + logger.error("Failed to retrieve users.") + raise TypeError(result) return result def get_user(self, name: str) -> dict: @@ -58,8 +60,8 @@ def get_user(self, name: str) -> dict: raise ValueError("name must not be empty") result = self.api.call_the_api(APIEndpoints.USER.value.format(name=name)) if not isinstance(result, dict): - logging.error(f"Failed to retrieve user: {name}") - raise Exception(result) + logger.error(f"Failed to retrieve user: {name}") + raise TypeError(result) return result def create_user(self, user: UserModel) -> dict: @@ -80,8 +82,8 @@ def create_user(self, user: UserModel) -> dict: json_complete=user.model_dump_json(by_alias=True, exclude_none=True), ) if not isinstance(result, dict): - logging.error("Failed to create user.") - raise Exception(result) + logger.error("Failed to create user.") + raise TypeError(result) return result def update_user(self, name: str, user: UserModel) -> dict: @@ -106,8 +108,8 @@ def update_user(self, name: str, user: UserModel) -> dict: json_complete=user.model_dump_json(by_alias=True, exclude_none=True), ) if not isinstance(result, dict): - logging.error(f"Failed to update user: {name}") - raise Exception(result) + logger.error(f"Failed to update user: {name}") + raise TypeError(result) return result def delete_user(self, name: str) -> None: diff --git a/perses_api/validate.py b/perses_api/validate.py index be50bf6..6ba340e 100644 --- a/perses_api/validate.py +++ b/perses_api/validate.py @@ -4,7 +4,9 @@ import logging from .api import Api -from .model import APIModel, APIEndpoints, RequestsMethods + +logger = logging.getLogger(__name__) +from .model import APIEndpoints, APIModel, RequestsMethods VALID_RESOURCE_TYPES = frozenset( { @@ -53,5 +55,5 @@ def validate(self, resource_type: str, body: dict) -> None: json_complete=json.dumps(body), ) if isinstance(result, dict) and result.get("code") and result["code"] >= 400: - logging.error(f"Validation failed for {resource_type}.") - raise Exception(result) + logger.error(f"Validation failed for {resource_type}.") + raise TypeError(result) diff --git a/perses_api/variable.py b/perses_api/variable.py index 4824355..abeefec 100644 --- a/perses_api/variable.py +++ b/perses_api/variable.py @@ -1,7 +1,7 @@ from __future__ import annotations from ._base import ResourceBase -from .model import APIModel, APIEndpoints +from .model import APIEndpoints, APIModel class VariableBase(ResourceBase): @@ -14,7 +14,7 @@ class VariableBase(ResourceBase): api (Api): This is where we store the api """ - def get_variables(self, name: str = None) -> list: + def get_variables(self, name: str | None = None) -> list: """The method includes a functionality to retrieve all variables Args: diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 806762a..b716176 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -1,30 +1,43 @@ import uuid + from perses_api import ( - Project, Dashboard, + Migrate, + Plugin, + Project, ProjectDatasource, - ProjectVariable, ProjectRole, + ProjectVariable, User, - Plugin, - Migrate, ) from perses_api.model import ( - Metadata, - ProjectSpec, - Project as ProjectModel, - DashboardSpec, Dashboard as DashboardModel, +) +from perses_api.model import ( + DashboardSpec, DatasourceSpec, - Datasource as DatasourceModel, - VariableSpec, - Variable as VariableModel, - RoleSpec, + Metadata, Permission, - Role as RoleModel, + ProjectSpec, + RoleSpec, UserSpec, + VariableSpec, +) +from perses_api.model import ( + Datasource as DatasourceModel, +) +from perses_api.model import ( + Project as ProjectModel, +) +from perses_api.model import ( + Role as RoleModel, +) +from perses_api.model import ( User as UserModel, ) +from perses_api.model import ( + Variable as VariableModel, +) def unique(prefix: str) -> str: diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index bdec7a3..1a34c13 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -1,5 +1,7 @@ import json + import pytest + from perses_api.api import Api from perses_api.model import APIModel, RequestsMethods @@ -79,7 +81,7 @@ def test_response_status_code_included(httpx_mock, model): def test_post_without_body_raises(model): api = Api(model) - with pytest.raises(Exception): + with pytest.raises(ValueError): api.call_the_api( "/api/v1/projects", method=RequestsMethods.POST, json_complete=None ) diff --git a/tests/unit/test_dashboard.py b/tests/unit/test_dashboard.py index 4f3b2f0..8bbd8c0 100644 --- a/tests/unit/test_dashboard.py +++ b/tests/unit/test_dashboard.py @@ -1,6 +1,7 @@ import pytest + from perses_api.dashboard import Dashboard -from perses_api.model import APIModel, Metadata, DashboardSpec +from perses_api.model import APIModel, DashboardSpec, Metadata from perses_api.model import Dashboard as DashboardModel diff --git a/tests/unit/test_datasource.py b/tests/unit/test_datasource.py index 64a912a..5f04875 100644 --- a/tests/unit/test_datasource.py +++ b/tests/unit/test_datasource.py @@ -1,6 +1,7 @@ import pytest -from perses_api.datasource import ProjectDatasource, GlobalDatasource -from perses_api.model import APIModel, Metadata, DatasourceSpec + +from perses_api.datasource import GlobalDatasource, ProjectDatasource +from perses_api.model import APIModel, DatasourceSpec, Metadata from perses_api.model import Datasource as DatasourceModel diff --git a/tests/unit/test_ephemeral_dashboard.py b/tests/unit/test_ephemeral_dashboard.py index b4226e1..80c0871 100644 --- a/tests/unit/test_ephemeral_dashboard.py +++ b/tests/unit/test_ephemeral_dashboard.py @@ -1,6 +1,7 @@ import pytest + from perses_api.ephemeral_dashboard import EphemeralDashboard -from perses_api.model import APIModel, Metadata, EphemeralDashboardSpec +from perses_api.model import APIModel, EphemeralDashboardSpec, Metadata from perses_api.model import EphemeralDashboard as EphemeralDashboardModel diff --git a/tests/unit/test_migrate.py b/tests/unit/test_migrate.py index a92bdc2..df73e37 100644 --- a/tests/unit/test_migrate.py +++ b/tests/unit/test_migrate.py @@ -1,6 +1,7 @@ import json import pytest + from perses_api.migrate import Migrate from perses_api.model import APIModel @@ -51,7 +52,7 @@ def test_migrate_result_missing_kind_raises(httpx_mock, model): error_response = {"message": "internal server error"} httpx_mock.add_response(json=error_response) client = Migrate(model) - with pytest.raises(Exception) as exc_info: + with pytest.raises(TypeError) as exc_info: client.migrate(grafana_dashboard={"title": "Test"}) assert exc_info.value.args[0] == error_response @@ -60,7 +61,7 @@ def test_migrate_result_missing_kind_with_message(httpx_mock, model, caplog): error_response = {"message": "Cue validation failed"} httpx_mock.add_response(json=error_response) client = Migrate(model) - with pytest.raises(Exception): + with pytest.raises(TypeError): client.migrate(grafana_dashboard={"title": "Test"}) assert "Migration failed: Cue validation failed" in caplog.text @@ -69,7 +70,7 @@ def test_migrate_result_missing_kind_without_message(httpx_mock, model, caplog): error_response = {"error": "some error"} httpx_mock.add_response(json=error_response) client = Migrate(model) - with pytest.raises(Exception) as exc_info: + with pytest.raises(TypeError) as exc_info: client.migrate(grafana_dashboard={"title": "Test"}) assert "Migration failed: Unknown error" in caplog.text assert exc_info.value.args[0] == error_response diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py index 1d13b2b..cec86ff 100644 --- a/tests/unit/test_model.py +++ b/tests/unit/test_model.py @@ -1,12 +1,12 @@ from perses_api.model import ( - APIModel, - RequestsMethods, APIEndpoints, + APIModel, + Dashboard, + DashboardSpec, Metadata, Project, ProjectSpec, - Dashboard, - DashboardSpec, + RequestsMethods, User, UserSpec, ) diff --git a/tests/unit/test_plugin.py b/tests/unit/test_plugin.py index fbfe541..5522fc8 100644 --- a/tests/unit/test_plugin.py +++ b/tests/unit/test_plugin.py @@ -1,6 +1,7 @@ import pytest -from perses_api.plugin import Plugin + from perses_api.model import APIModel +from perses_api.plugin import Plugin @pytest.fixture diff --git a/tests/unit/test_project.py b/tests/unit/test_project.py index cca4a3c..1c3823c 100644 --- a/tests/unit/test_project.py +++ b/tests/unit/test_project.py @@ -1,7 +1,8 @@ import pytest -from perses_api.project import Project + from perses_api.model import APIModel, Metadata, ProjectSpec from perses_api.model import Project as ProjectModel +from perses_api.project import Project @pytest.fixture diff --git a/tests/unit/test_role.py b/tests/unit/test_role.py index 001c3d2..9ecf6aa 100644 --- a/tests/unit/test_role.py +++ b/tests/unit/test_role.py @@ -1,7 +1,8 @@ import pytest -from perses_api.role import ProjectRole, GlobalRole -from perses_api.model import APIModel, Metadata, RoleSpec, Permission + +from perses_api.model import APIModel, Metadata, Permission, RoleSpec from perses_api.model import Role as RoleModel +from perses_api.role import GlobalRole, ProjectRole @pytest.fixture diff --git a/tests/unit/test_role_binding.py b/tests/unit/test_role_binding.py index aa22b36..7313f84 100644 --- a/tests/unit/test_role_binding.py +++ b/tests/unit/test_role_binding.py @@ -1,7 +1,8 @@ import pytest -from perses_api.role_binding import ProjectRoleBinding, GlobalRoleBinding + from perses_api.model import APIModel, Metadata, RoleBindingSpec, Subject from perses_api.model import RoleBinding as RoleBindingModel +from perses_api.role_binding import GlobalRoleBinding, ProjectRoleBinding @pytest.fixture diff --git a/tests/unit/test_secret.py b/tests/unit/test_secret.py index 20faebd..98ad677 100644 --- a/tests/unit/test_secret.py +++ b/tests/unit/test_secret.py @@ -1,7 +1,8 @@ import pytest -from perses_api.secret import ProjectSecret, GlobalSecret + from perses_api.model import APIModel, Metadata, SecretSpec from perses_api.model import Secret as SecretModel +from perses_api.secret import GlobalSecret, ProjectSecret @pytest.fixture diff --git a/tests/unit/test_user.py b/tests/unit/test_user.py index b70a707..348167b 100644 --- a/tests/unit/test_user.py +++ b/tests/unit/test_user.py @@ -1,7 +1,8 @@ import pytest -from perses_api.user import User + from perses_api.model import APIModel, Metadata, UserSpec from perses_api.model import User as UserModel +from perses_api.user import User @pytest.fixture diff --git a/tests/unit/test_validate.py b/tests/unit/test_validate.py index 5aa1de1..0972240 100644 --- a/tests/unit/test_validate.py +++ b/tests/unit/test_validate.py @@ -1,6 +1,7 @@ import pytest -from perses_api.validate import Validate + from perses_api.model import APIModel +from perses_api.validate import Validate @pytest.fixture diff --git a/tests/unit/test_variable.py b/tests/unit/test_variable.py index b87b07f..e47ceb9 100644 --- a/tests/unit/test_variable.py +++ b/tests/unit/test_variable.py @@ -1,7 +1,8 @@ import pytest -from perses_api.variable import ProjectVariable, GlobalVariable + from perses_api.model import APIModel, Metadata, VariableSpec from perses_api.model import Variable as VariableModel +from perses_api.variable import GlobalVariable, ProjectVariable @pytest.fixture