rabbitmq-management-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. rabbitmq_management_sdk-0.1.0/PKG-INFO +37 -0
  2. rabbitmq_management_sdk-0.1.0/README.md +14 -0
  3. rabbitmq_management_sdk-0.1.0/pyproject.toml +121 -0
  4. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/__init__.py +44 -0
  5. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/client/__init__.py +0 -0
  6. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/client/config.py +128 -0
  7. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/client/rabbitmq_client.py +149 -0
  8. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/client/utils.py +34 -0
  9. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/__init__.py +0 -0
  10. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/base.py +50 -0
  11. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/__init__.py +0 -0
  12. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/admin/__init__.py +0 -0
  13. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/admin/schemas/__init__.py +0 -0
  14. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/admin/schemas/vhost_request.py +37 -0
  15. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/admin/schemas/vhost_response.py +35 -0
  16. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/admin/services.py +55 -0
  17. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/bindings/__init__.py +0 -0
  18. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/bindings/schemas/__init__.py +0 -0
  19. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/bindings/schemas/binding_request.py +28 -0
  20. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/bindings/schemas/binding_response.py +30 -0
  21. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/bindings/schemas/common.py +8 -0
  22. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/bindings/services.py +199 -0
  23. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/exchanges/__init__.py +0 -0
  24. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/exchanges/schemas/__init__.py +0 -0
  25. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/exchanges/schemas/common.py +20 -0
  26. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/exchanges/schemas/exchange_request.py +49 -0
  27. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/exchanges/schemas/exchange_response.py +44 -0
  28. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/exchanges/services.py +98 -0
  29. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/queues/__init__.py +0 -0
  30. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/queues/schemas/__init__.py +0 -0
  31. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/queues/schemas/queue_request.py +168 -0
  32. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/queues/schemas/queue_response.py +131 -0
  33. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/queues/services.py +50 -0
  34. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/domains/v4/shovels/__init__.py +0 -0
  35. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/exceptions.py +171 -0
  36. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/http_adapter/__init__.py +18 -0
  37. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/http_adapter/base.py +52 -0
  38. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/http_adapter/config.py +61 -0
  39. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/http_adapter/factory.py +39 -0
  40. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/http_adapter/httpx.py +114 -0
  41. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/http_adapter/retry.py +75 -0
  42. rabbitmq_management_sdk-0.1.0/src/rabbitmq_management_sdk/py.typed +0 -0
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: rabbitmq-management-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the RabbitMQ Management HTTP API to manage queues, exchanges, bindings, users, and policies.
5
+ Keywords: rabbitmq,rabbitmq management api,rabbitmq http api,rabbitmq admin,rabbitmq management,rabbitmq provisioning
6
+ Author: brianmori
7
+ Author-email: brianmori <52781411+brianmori@users.noreply.github.com>
8
+ License-Expression: Apache-2.0
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3.14
12
+ Classifier: Typing :: Typed
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Classifier: Topic :: System :: Systems Administration
15
+ Requires-Dist: pydantic>=2.12.5,<3.0.0
16
+ Requires-Dist: httpx>=0.28.1,<1.0.0
17
+ Requires-Python: >=3.14
18
+ Project-URL: Homepage, https://github.com/brianmori/rabbitmq-management-sdk
19
+ Project-URL: Documentation, https://brianmori.github.io/rabbitmq-management-sdk
20
+ Project-URL: Repository, https://github.com/brianmori/rabbitmq-management-sdk
21
+ Project-URL: Issues, https://github.com/brianmori/rabbitmq-management-sdk/issues
22
+ Description-Content-Type: text/markdown
23
+
24
+ # rabbitmq-admin-sdk
25
+
26
+ A modern, typed Python SDK for the RabbitMQ HTTP Management API.
27
+
28
+ ## Development setup
29
+
30
+ This project uses [uv](https://docs.astral.sh/uv/) for dependency and environment management.
31
+
32
+ ```bash
33
+ uv sync --group dev
34
+ ```
35
+
36
+ That command creates/syncs the local environment with all development dependencies needed for linting, typing, and
37
+ tests (for example: `ruff`, `pytest`, `mypy`, and `python-dotenv`).
@@ -0,0 +1,14 @@
1
+ # rabbitmq-admin-sdk
2
+
3
+ A modern, typed Python SDK for the RabbitMQ HTTP Management API.
4
+
5
+ ## Development setup
6
+
7
+ This project uses [uv](https://docs.astral.sh/uv/) for dependency and environment management.
8
+
9
+ ```bash
10
+ uv sync --group dev
11
+ ```
12
+
13
+ That command creates/syncs the local environment with all development dependencies needed for linting, typing, and
14
+ tests (for example: `ruff`, `pytest`, `mypy`, and `python-dotenv`).
@@ -0,0 +1,121 @@
1
+ [project]
2
+ name = "rabbitmq-management-sdk"
3
+ version = "0.1.0"
4
+ description = "Python SDK for the RabbitMQ Management HTTP API to manage queues, exchanges, bindings, users, and policies."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "brianmori", email = "52781411+brianmori@users.noreply.github.com" }
8
+ ]
9
+ license = "Apache-2.0"
10
+
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Developers",
14
+ "Programming Language :: Python :: 3.14",
15
+ "Typing :: Typed",
16
+ "Topic :: Software Development :: Libraries :: Python Modules",
17
+ "Topic :: System :: Systems Administration",
18
+ ]
19
+ keywords = [
20
+ "rabbitmq",
21
+ "rabbitmq management api",
22
+ "rabbitmq http api",
23
+ "rabbitmq admin",
24
+ "rabbitmq management",
25
+ "rabbitmq provisioning",
26
+ ]
27
+
28
+ requires-python = ">=3.14"
29
+ dependencies = [
30
+ "pydantic>=2.12.5,<3.0.0",
31
+ "httpx>=0.28.1,<1.0.0",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/brianmori/rabbitmq-management-sdk"
36
+ Documentation = "https://brianmori.github.io/rabbitmq-management-sdk"
37
+ Repository = "https://github.com/brianmori/rabbitmq-management-sdk"
38
+ Issues = "https://github.com/brianmori/rabbitmq-management-sdk/issues"
39
+
40
+ [build-system]
41
+ requires = ["uv_build>=0.11.1,<0.12.0"]
42
+ build-backend = "uv_build"
43
+
44
+ [dependency-groups]
45
+ dev = [
46
+ "ruff>=0.15.2,<1.0.0",
47
+ "pytest>=9.0.3,<10.0.0",
48
+ "pytest-cov>=7.1.0,<8.0.0",
49
+ "mypy>=2.1.0,<3.0.0",
50
+ "pip-audit>=2.10.0,<3.0.0",
51
+ "python-dotenv>=1.2.2,<2.0.0",
52
+ ]
53
+
54
+ [tool.ruff]
55
+ line-length = 120
56
+ target-version = "py314"
57
+ src = ["src", "tests"]
58
+
59
+ [tool.ruff.lint]
60
+ select = [
61
+ "E", # pycodestyle errors
62
+ "W", # pycodestyle warnings
63
+ "F", # pyflakes
64
+ "I", # isort
65
+ "B", # flake8-bugbear
66
+ "C4", # flake8-comprehensions
67
+ "UP", # pyupgrade
68
+ "SIM", # flake8-simplify
69
+ "TCH", # flake8-type-checking — moves pure-annotation imports into TYPE_CHECKING blocks
70
+ "ANN", # flake8-annotations — enforces type hint coverage
71
+ "PTH", # flake8-use-pathlib — prefers pathlib over os.path
72
+ "RUF", # ruff-specific rules
73
+ "PL", # pylint
74
+ "TID", # flake8-tidy-imports
75
+ ]
76
+
77
+ [tool.ruff.format]
78
+ quote-style = "double"
79
+ indent-style = "space"
80
+ docstring-code-format = true
81
+
82
+ [tool.ruff.lint.pydocstyle]
83
+ convention = "google"
84
+
85
+ [tool.ruff.lint.isort]
86
+ known-first-party = ["rabbitmq_management_sdk"]
87
+
88
+ [tool.ruff.lint.pylint]
89
+ max-args = 8
90
+
91
+ [tool.ruff.lint.flake8-tidy-imports]
92
+ ban-relative-imports = "all"
93
+
94
+ [tool.ruff.lint.flake8-type-checking]
95
+ runtime-evaluated-base-classes = ["rabbitmq_management_sdk.domains.base.RabbitMQBase"]
96
+
97
+ [tool.ruff.lint.per-file-ignores]
98
+ "tests/**/*" = ["PLR2004"]
99
+
100
+ [tool.uv.workspace]
101
+ members = [
102
+ "rabbitmq-management-sdk",
103
+ ]
104
+
105
+ [tool.pytest.ini_options]
106
+ pythonpath = ["src"]
107
+ testpaths = ["tests"]
108
+ markers = [
109
+ "unit: Unit tests (fast, no I/O)",
110
+ "integration: Integration tests with mocked HTTP (fast, no real I/O)",
111
+ "live: Live tests against a real RabbitMQ instance (slow, requires Docker)",
112
+ ]
113
+ env_files = [".env"]
114
+ addopts = "--import-mode=importlib"
115
+
116
+ [tool.mypy]
117
+ python_version = "3.14"
118
+ strict = true
119
+ files = ["src", "tests"]
120
+ ignore_missing_imports = true
121
+ plugins = ["pydantic.mypy"]
@@ -0,0 +1,44 @@
1
+ from rabbitmq_management_sdk.client.config import Config, SSLConfig
2
+ from rabbitmq_management_sdk.client.rabbitmq_client import RabbitMQClient
3
+ from rabbitmq_management_sdk.exceptions import (
4
+ APIError,
5
+ BadRequestError,
6
+ ConflictError,
7
+ ConnectionError,
8
+ ForbiddenError,
9
+ MalformedResponseError,
10
+ MethodNotAllowedError,
11
+ NotFoundError,
12
+ PreconditionFailedError,
13
+ RabbitMQError,
14
+ ServerError,
15
+ ServiceUnavailableError,
16
+ TimeoutError,
17
+ TooManyRequestsError,
18
+ TransportError,
19
+ UnauthorizedError,
20
+ UnprocessableEntityError,
21
+ )
22
+
23
+ __all__ = [
24
+ "APIError",
25
+ "BadRequestError",
26
+ "Config",
27
+ "ConflictError",
28
+ "ConnectionError",
29
+ "ForbiddenError",
30
+ "MalformedResponseError",
31
+ "MethodNotAllowedError",
32
+ "NotFoundError",
33
+ "PreconditionFailedError",
34
+ "RabbitMQClient",
35
+ "RabbitMQError",
36
+ "SSLConfig",
37
+ "ServerError",
38
+ "ServiceUnavailableError",
39
+ "TimeoutError",
40
+ "TooManyRequestsError",
41
+ "TransportError",
42
+ "UnauthorizedError",
43
+ "UnprocessableEntityError",
44
+ ]
@@ -0,0 +1,128 @@
1
+ import ssl
2
+ from enum import IntEnum
3
+ from urllib.parse import quote
4
+
5
+ from pydantic import BaseModel, Field, FilePath, SecretStr, model_validator
6
+
7
+
8
+ class SSLConfig(BaseModel):
9
+ """SSLContext for internal PKI or mutual TLS.
10
+
11
+ Attributes:
12
+ ca_bundle: Path to a custom CA bundle (.pem) for internal PKI.
13
+ client_cert: Tuple of (cert_path, key_path) for mTLS.
14
+ verify: Set to False to disable verification entirely (dev only).
15
+ min_version: Minimum TLS version. Defaults to TLSv1.2.
16
+ """
17
+
18
+ verify: bool = True
19
+ # FilePath validates that the file actually exists on the system
20
+ ca_bundle: FilePath | None = None
21
+ # Tuple of (cert_file, key_file)
22
+ client_cert: tuple[FilePath, FilePath] | None = None
23
+ min_version: ssl.TLSVersion = Field(
24
+ default=ssl.TLSVersion.TLSv1_2, description="Minimum TLS version. Defaults to TLSv1.2."
25
+ )
26
+
27
+
28
+ class Config(BaseModel):
29
+ """Configuration for the RabbitMQ management API client.
30
+
31
+ Attributes:
32
+ host: Hostname or IP of the RabbitMQ node.
33
+ port: Management API port. Defaults to 15672 (plain).
34
+ username: RabbitMQ management user.
35
+ password: Password for the management user.
36
+ ssl_context: TLS settings. When None the connection uses plain HTTP.
37
+ virtual_host: Default virtual host for vhost-scoped API calls. Defaults to '/'.
38
+ version_override: Pin a specific RabbitMQ version (e.g. '4.3.0') instead of relying on automatic detection.
39
+ """
40
+
41
+ host: str = Field(description="Hostname or IP of the RabbitMQ node.")
42
+ port: int = Field(
43
+ default=15672,
44
+ gt=1024,
45
+ lt=65536,
46
+ description="Management API port. Defaults to 15672 (plain).",
47
+ )
48
+
49
+ username: str | None = Field(description="RabbitMQ management user.")
50
+ password: SecretStr | None = Field(description="Password for the management user.")
51
+
52
+ ssl_context: SSLConfig | None = Field(
53
+ default=None,
54
+ description="TLS settings. When None the connection uses plain HTTP.",
55
+ )
56
+
57
+ virtual_host: str = Field(
58
+ default="/",
59
+ description="Defaults to '/', the RabbitMQ built-in virtual host.",
60
+ )
61
+
62
+ @property
63
+ def virtual_host_safe(self) -> str:
64
+ """Return the virtual host name URL-encoded for use in API paths.
65
+
66
+ The result is percent-encoded with ``safe=""`` so that ``/`` becomes
67
+ ``%2F``. Managers receive this value and must NOT encode it again.
68
+ """
69
+ return quote(self.virtual_host, safe="")
70
+
71
+ strict: bool = Field(
72
+ default=False,
73
+ description="If True, all default arguments are sent explicitly to RabbitMQ. "
74
+ "Note: Enabling this on existing queues will cause a 406 Precondition Failed "
75
+ "conflict if they were originally created without explicit defaults.",
76
+ )
77
+
78
+ version_override: RabbitMQVersion | None = Field(
79
+ default=None,
80
+ description=(
81
+ "Pin a specific RabbitMQ version string (e.g. '4.3.0') instead of "
82
+ "relying on automatic detection. Useful when the management API is "
83
+ "behind a proxy that strips version headers."
84
+ ),
85
+ )
86
+
87
+ @property
88
+ def base_url(self) -> str:
89
+ """The base URL for API calls."""
90
+ scheme = "https" if self.ssl_context is not None else "http"
91
+ return f"{scheme}://{self.host}:{self.port}/api"
92
+
93
+ @model_validator(mode="after")
94
+ def validate_config(self) -> Config:
95
+ has_basic_auth = self.username is not None and self.password is not None
96
+ has_mtls = self.ssl_context is not None and self.ssl_context.client_cert is not None
97
+
98
+ if not (has_basic_auth or has_mtls):
99
+ raise ValueError("Provide either username/password or a client certificate.")
100
+
101
+ if has_basic_auth and has_mtls:
102
+ raise ValueError("Choose Basic Auth (username/password) or mTLS (client_cert). ")
103
+
104
+ return self
105
+
106
+
107
+ class RabbitMQVersion(BaseModel):
108
+ """Semantic version of the RabbitMQ server."""
109
+
110
+ major: int
111
+ minor: int
112
+ patch: int
113
+
114
+ @classmethod
115
+ def parse(cls, version: str) -> RabbitMQVersion:
116
+ try:
117
+ major, minor, patch = version.split(".")
118
+ return cls(major=int(major), minor=int(minor), patch=int(patch))
119
+ except ValueError:
120
+ raise ValueError(f"Unrecognised RabbitMQ version string: {version!r}") from None
121
+
122
+ def __str__(self) -> str:
123
+ return f"{self.major}.{self.minor}.{self.patch}"
124
+
125
+
126
+ class RabbitMQMajorVersion(IntEnum):
127
+ V4 = 4
128
+ V5 = 5
@@ -0,0 +1,149 @@
1
+ import logging
2
+ from http import HTTPMethod
3
+ from typing import TYPE_CHECKING
4
+
5
+ from rabbitmq_management_sdk.client.config import RabbitMQMajorVersion, RabbitMQVersion
6
+ from rabbitmq_management_sdk.client.utils import create_ssl_context
7
+ from rabbitmq_management_sdk.domains.v4.admin.services import AdminManagerV4
8
+ from rabbitmq_management_sdk.domains.v4.bindings.services import BindingManagerV4
9
+ from rabbitmq_management_sdk.domains.v4.exchanges.services import ExchangeManagerV4
10
+ from rabbitmq_management_sdk.domains.v4.queues.services import QueueManagerV4
11
+ from rabbitmq_management_sdk.exceptions import MalformedResponseError, RabbitMQError
12
+ from rabbitmq_management_sdk.http_adapter import HttpAdapter, HttpResponse, factory
13
+ from rabbitmq_management_sdk.http_adapter.config import BasicAuthentication
14
+
15
+ if TYPE_CHECKING:
16
+ import ssl
17
+
18
+ from rabbitmq_management_sdk.client.config import Config
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class RabbitMQClient:
24
+ """Client for the RabbitMQ Management API.
25
+
26
+ Attributes:
27
+ _config: Configuration for the client.
28
+ _ha: HTTP adapter for making API requests.
29
+ _basic_auth: Basic authentication for API requests.
30
+ _version: Semantic version of the RabbitMQ server.
31
+ """
32
+
33
+ def __init__(self, config: Config) -> None:
34
+ self._config = config
35
+ self._ha: HttpAdapter
36
+ self._basic_auth: BasicAuthentication
37
+
38
+ default_headers: dict[str, str] = {}
39
+ ssl_context: ssl.SSLContext | None = None
40
+
41
+ if config.password and config.username:
42
+ self._basic_auth = BasicAuthentication(
43
+ username=config.username, password=config.password.get_secret_value()
44
+ )
45
+ default_headers["Authorization"] = self._basic_auth.auth_header
46
+
47
+ if config.ssl_context:
48
+ ssl_context = create_ssl_context(config.ssl_context)
49
+
50
+ self._ha = factory.create_adapter(
51
+ host=config.host, port=config.port, default_headers=default_headers, ssl_context=ssl_context
52
+ )
53
+
54
+ self._version: RabbitMQVersion = self._get_version()
55
+
56
+ logger.debug(
57
+ "RabbitMQClient initialized",
58
+ extra={"host": config.host, "port": config.port, "version": self._version},
59
+ )
60
+
61
+ def _get_version(self) -> RabbitMQVersion:
62
+ """
63
+ Retrieves the RabbitMQ version from the server.
64
+
65
+ Returns:
66
+ RabbitMQVersion: The semantic version of the RabbitMQ server.
67
+
68
+ Raises:
69
+ RabbitMQError: If there is a problem communicating with the server or parsing the version.
70
+ """
71
+
72
+ if self._config.version_override is not None:
73
+ return self._config.version_override
74
+
75
+ try:
76
+ hr: HttpResponse = self._ha.request(method=HTTPMethod.GET, path="/api/overview")
77
+ data = hr.json()
78
+ except RabbitMQError as e:
79
+ logger.error(
80
+ "Failed to reach RabbitMQ Management API during version detection. "
81
+ "Consider setting version_override in Config and/or proxy settings.",
82
+ extra={"host": self._config.host, "port": self._config.port},
83
+ exc_info=e,
84
+ )
85
+ raise
86
+
87
+ rabbitmq_version = data.get("rabbitmq_version")
88
+
89
+ if not isinstance(rabbitmq_version, str):
90
+ logger.error(
91
+ "RabbitMQ version field missing or not a string in /api/overview response. "
92
+ "Consider setting version_override in Config.",
93
+ extra={
94
+ "host": self._config.host,
95
+ "port": self._config.port,
96
+ "rabbitmq_version": rabbitmq_version,
97
+ },
98
+ )
99
+ raise MalformedResponseError(
100
+ f"Expected a string for rabbitmq_version, got {type(rabbitmq_version).__name__}. "
101
+ f"Set version_override in Config to bypass detection."
102
+ )
103
+
104
+ try:
105
+ return RabbitMQVersion.parse(rabbitmq_version)
106
+ except ValueError as e:
107
+ logger.error(
108
+ "RabbitMQ version string could not be parsed.",
109
+ extra={
110
+ "host": self._config.host,
111
+ "port": self._config.port,
112
+ "rabbitmq_version": rabbitmq_version,
113
+ },
114
+ exc_info=e,
115
+ )
116
+ raise MalformedResponseError(
117
+ f"Could not parse rabbitmq_version {rabbitmq_version!r}. "
118
+ f"Set version_override in Config to bypass detection."
119
+ ) from e
120
+
121
+ @property
122
+ def queues(self) -> QueueManagerV4:
123
+ if self._version.major == RabbitMQMajorVersion.V4:
124
+ return QueueManagerV4(
125
+ http_client=self._ha, vhost=self._config.virtual_host_safe, strict=self._config.strict
126
+ )
127
+ raise NotImplementedError(f"Version {self._version} not supported")
128
+
129
+ @property
130
+ def admin(self) -> AdminManagerV4:
131
+ if self._version.major == RabbitMQMajorVersion.V4:
132
+ return AdminManagerV4(http_client=self._ha, strict=self._config.strict)
133
+ raise NotImplementedError(f"Version {self._version} not supported")
134
+
135
+ @property
136
+ def exchanges(self) -> ExchangeManagerV4:
137
+ if self._version.major == RabbitMQMajorVersion.V4:
138
+ return ExchangeManagerV4(
139
+ http_client=self._ha, vhost=self._config.virtual_host_safe, strict=self._config.strict
140
+ )
141
+ raise NotImplementedError(f"Version {self._version} not supported")
142
+
143
+ @property
144
+ def bindings(self) -> BindingManagerV4:
145
+ if self._version.major == RabbitMQMajorVersion.V4:
146
+ return BindingManagerV4(
147
+ http_client=self._ha, vhost=self._config.virtual_host_safe, strict=self._config.strict
148
+ )
149
+ raise NotImplementedError(f"Version {self._version} not supported")
@@ -0,0 +1,34 @@
1
+ import ssl
2
+ from typing import TYPE_CHECKING
3
+
4
+ if TYPE_CHECKING:
5
+ from rabbitmq_management_sdk.client.config import SSLConfig
6
+
7
+
8
+ def create_ssl_context(sc: SSLConfig) -> ssl.SSLContext:
9
+ """Creates a SSL context for the RabbitMQ Management API.
10
+
11
+ TLS Version defaults to TLSv1.2 and hostname verification is enabled.
12
+
13
+ Args:
14
+ sc: SSL configuration for the client.
15
+
16
+ Returns:
17
+ An SSL context configured with the provided SSL settings.
18
+ """
19
+ ctx = ssl.create_default_context(
20
+ purpose=ssl.Purpose.SERVER_AUTH,
21
+ cafile=sc.ca_bundle,
22
+ )
23
+ ctx.verify_mode = ssl.CERT_REQUIRED
24
+ ctx.minimum_version = sc.min_version
25
+ ctx.check_hostname = True
26
+
27
+ if not sc.verify:
28
+ ctx.check_hostname = False
29
+ ctx.verify_mode = ssl.CERT_NONE
30
+
31
+ if sc.client_cert:
32
+ ctx.load_cert_chain(*sc.client_cert)
33
+
34
+ return ctx
@@ -0,0 +1,50 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from pydantic import BaseModel, ConfigDict, ValidationError
4
+
5
+ from rabbitmq_management_sdk.exceptions import MalformedResponseError
6
+
7
+ if TYPE_CHECKING:
8
+ from rabbitmq_management_sdk.http_adapter.base import HttpResponse
9
+
10
+
11
+ class RabbitMQBase(BaseModel):
12
+ model_config = ConfigDict(extra="ignore", frozen=True, populate_by_name=True, use_enum_values=True)
13
+
14
+
15
+ def parse_one[T: RabbitMQBase](response: HttpResponse, model: type[T]) -> T:
16
+ """Parse a single model from an HTTP response body.
17
+
18
+ Args:
19
+ response: The HTTP response to parse.
20
+ model: The Pydantic model class to validate against.
21
+
22
+ Returns:
23
+ A validated model instance.
24
+
25
+ Raises:
26
+ MalformedResponseError: If the body cannot be decoded or does not match the schema.
27
+ """
28
+ try:
29
+ return model.model_validate(response.json())
30
+ except ValidationError as e:
31
+ raise MalformedResponseError(f"Response body did not match {model.__name__}") from e
32
+
33
+
34
+ def parse_list[T: RabbitMQBase](response: HttpResponse, model: type[T]) -> list[T]:
35
+ """Parse a list of models from an HTTP response body.
36
+
37
+ Args:
38
+ response: The HTTP response to parse.
39
+ model: The Pydantic model class to validate each item against.
40
+
41
+ Returns:
42
+ A list of validated model instances.
43
+
44
+ Raises:
45
+ MalformedResponseError: If the body cannot be decoded or an item does not match the schema.
46
+ """
47
+ try:
48
+ return [model.model_validate(item) for item in response.json()]
49
+ except ValidationError as e:
50
+ raise MalformedResponseError(f"Response body did not match list[{model.__name__}]") from e
@@ -0,0 +1,37 @@
1
+ # domains/v4/vhost/schemas/vhost_request.py
2
+ from enum import StrEnum
3
+ from typing import Literal
4
+
5
+ from pydantic import Field
6
+
7
+ from rabbitmq_management_sdk.domains.base import RabbitMQBase
8
+
9
+
10
+ class VhostLimitName(StrEnum):
11
+ """Valid vhost limit names accepted by the RabbitMQ Management API."""
12
+
13
+ MAX_CONNECTIONS = "max-connections"
14
+ MAX_QUEUES = "max-queues"
15
+
16
+
17
+ class VhostRequest(RabbitMQBase):
18
+ description: str | None = None
19
+ tags: list[str] = Field(default_factory=list)
20
+ tracing: bool = False
21
+ default_queue_type: Literal["classic", "quorum", "stream"] = "classic"
22
+
23
+
24
+ class VhostLimitRequest(RabbitMQBase):
25
+ """Payload for ``PUT /api/vhost-limits/{vhost}/{limit}``.
26
+
27
+ The vhost and limit name are part of the URL — only the value is sent
28
+ in the request body.
29
+
30
+ Attributes:
31
+ value (int): The value to be set for the specific vhost limit.
32
+
33
+ Example:
34
+ >>> VhostLimitRequest(value=100)
35
+ """
36
+
37
+ value: int = Field(ge=-1, description="Limit must be -1 (unlimited), 0 (block) or a positive integer.")
@@ -0,0 +1,35 @@
1
+ # domains/v4/vhost/schemas/vhost_response.py
2
+ from typing import Literal
3
+
4
+ from pydantic import Field
5
+
6
+ from rabbitmq_management_sdk.domains.base import RabbitMQBase
7
+
8
+
9
+ class VhostResponse(RabbitMQBase):
10
+ name: str
11
+ description: str | None = None
12
+ tags: list[str] = Field(default_factory=list)
13
+ tracing: bool
14
+ default_queue_type: Literal["classic", "quorum", "stream"] | None = None
15
+ cluster_state: dict[str, str] | None = None
16
+
17
+
18
+ class VhostLimitValues(RabbitMQBase):
19
+ max_connections: int | None = Field(alias="max-connections", default=None)
20
+ max_queues: int | None = Field(alias="max-queues", default=None)
21
+
22
+
23
+ class VhostLimitResponse(RabbitMQBase):
24
+ """A single vhost limit as returned by the Management API.
25
+
26
+ Returned by both `GET /api/vhost-limits/{vhost}` and `GET /api/vhost-limits`.
27
+ The `vhost` field is always present, which is useful when listing limits
28
+ across the entire cluster.
29
+
30
+ Example:
31
+ >>> {"vhost": "test", "value": {"max-connections": 3, "max-queues": 3}}
32
+ """
33
+
34
+ vhost: str
35
+ value: VhostLimitValues