pyznuny 0.0.1__py3-none-any.whl

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.
pyznuny/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .ticket.client import TicketClient
2
+
3
+ __all__ = ["TicketClient"]
@@ -0,0 +1,7 @@
1
+ from .client import TicketClient, SessionRoutes
2
+ from .endpoints import Endpoint, EndpointsRegistry
3
+ from .models import TicketCreateArticle, TicketCreatePayload, TicketCreateTicket
4
+
5
+ __all__ = [
6
+ "TicketClient",
7
+ ]
@@ -0,0 +1,261 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from typing import Any
5
+
6
+ import httpx
7
+
8
+ if __name__ == "__main__" and __package__ is None:
9
+ import os
10
+ import sys
11
+
12
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
13
+ from pyznuny.ticket.endpoints import Endpoint, EndpointsRegistry,EndpointSetter, _DEFAULT_ENDPOINT_IDENTIFIERS, _DEFAULT_ENDPOINTS
14
+ from pyznuny.ticket.models import (
15
+ TicketCreateArticle,
16
+ TicketCreatePayload,
17
+ TicketCreateTicket,
18
+ )
19
+ from pyznuny.ticket.exceptions import TicketClientError
20
+ from pyznuny.ticket.routes import TicketRoutes, SessionRoutes
21
+ else:
22
+ from .endpoints import Endpoint, EndpointsRegistry,EndpointSetter, _DEFAULT_ENDPOINT_IDENTIFIERS, _DEFAULT_ENDPOINTS
23
+ from .models import (
24
+ TicketCreateArticle,
25
+ TicketCreatePayload,
26
+ TicketCreateTicket,
27
+ )
28
+ from .routes import TicketRoutes, SessionRoutes
29
+ from .exceptions import TicketClientError
30
+
31
+
32
+
33
+
34
+ class TicketClient:
35
+ """
36
+ Represents a client for interacting with the Ticket API
37
+
38
+ :param base_url: Base URL for the Ticket API which the client will connect to
39
+ :type base_url: str | None
40
+ :param username: Username for authentication
41
+ :type username: str | None
42
+ :param password: Password for authentication
43
+ :type password: str | None
44
+ :param endpoints: Optional custom endpoints registry
45
+ :type endpoints: EndpointsRegistry | None
46
+ :param timeout: Request timeout
47
+ :type timeout: float | None
48
+ :param headers: Optional custom headers
49
+ :type headers: Mapping[str, str] | None
50
+ """
51
+ def __init__(
52
+ self,
53
+ base_url: str | None = None,
54
+ *,
55
+ username: str | None = None,
56
+ password: str | None = None,
57
+ endpoints: EndpointsRegistry | None = None,
58
+ timeout: float | None = None,
59
+ headers: Mapping[str, str] | None = None,
60
+ client: httpx.Client | None = None,
61
+ ) -> None:
62
+ self._endpoints = endpoints or EndpointsRegistry()
63
+ self._endpoint_identifiers = dict(_DEFAULT_ENDPOINT_IDENTIFIERS)
64
+ if client is not None:
65
+ self._client = client
66
+ else:
67
+ client_kwargs: dict[str, Any] = {"timeout": timeout, "headers": headers}
68
+ if base_url is not None:
69
+ client_kwargs["base_url"] = base_url
70
+ self._client = httpx.Client(**client_kwargs)
71
+
72
+ self._register_default_endpoints()
73
+ self.ticket = TicketRoutes(self)
74
+ self.session = SessionRoutes(self)
75
+ self.set_endpoint = EndpointSetter(self)
76
+ self.session_id: str | None = None
77
+
78
+ if username is not None and password is not None:
79
+ self.login(username, password)
80
+
81
+ @property
82
+ def endpoints(self) -> EndpointsRegistry:
83
+ return self._endpoints
84
+
85
+ def register_endpoint(self, name: str, method: str, path: str) -> Endpoint:
86
+ """
87
+ Registers a custom endpoint for the Ticket API
88
+
89
+ :param name: Name of the endpoint
90
+ :type name: str
91
+ :param method: HTTP method for the endpoint
92
+ :type method: str
93
+ :param path: Endpoint path
94
+ :type path: str
95
+ :return: Registered endpoint
96
+ :rtype: Endpoint
97
+ """
98
+ return self._endpoints.register(Endpoint(name=name, method=method, path=path))
99
+
100
+ def set_endpoint_identifier(self, name: str, identifier: str) -> None:
101
+ """
102
+ Sets a custom identifier for an endpoint
103
+
104
+ :param name: Name of the endpoint
105
+ :type name: str
106
+ :param identifier: Custom identifier for the endpoint
107
+ :type identifier: str
108
+ """
109
+ self._endpoint_identifiers[name] = identifier
110
+
111
+ def endpoint_identifier(self, name: str) -> str:
112
+ """
113
+ Returns the custom identifier for an endpoint
114
+
115
+ :param name: Name of the endpoint
116
+ :type name: str
117
+ :return: Custom identifier for the endpoint
118
+ :rtype: str
119
+ """
120
+ try:
121
+ return self._endpoint_identifiers[name]
122
+ except KeyError as exc:
123
+ raise KeyError(f"Endpoint identifier not registered: {name}") from exc
124
+
125
+ def login(self, username: str, password: str) -> httpx.Response:
126
+ """
127
+ Creates a new session with the given username and password.
128
+
129
+ :param username: Username for authentication
130
+ :type username: str
131
+ :param password: Password for authentication
132
+ :type password: str
133
+ :return: Response object containing session details
134
+ :rtype: Response
135
+ """
136
+ response = self.session.create(username, password)
137
+ self.session_id = response.json().get("SessionID")
138
+
139
+
140
+ def request(
141
+ self,
142
+ endpoint_name: str,
143
+ *,
144
+ method: str | None = None,
145
+ path: str | None = None,
146
+ path_params: Mapping[str, Any] | None = None,
147
+ **kwargs: Any,
148
+ ) -> httpx.Response:
149
+ """
150
+ Sends a request to the Ticket API
151
+
152
+ :param endpoint_name: Name of the endpoint
153
+ :type endpoint_name: str
154
+ :param method: HTTP method for the request, defaults to None
155
+ :type method: str | None
156
+ :param path: Custom endpoint path, defaults to None
157
+ :type path: str | None
158
+ :param path_params: Parameters for the endpoint path, defaults to None
159
+ :type path_params: Mapping[str, Any] | None
160
+ :param kwargs: Additional keyword arguments for the request
161
+ :type kwargs: Any
162
+ :return: Response object
163
+ :rtype: httpx.Response
164
+ """
165
+ endpoint_method = method or self._endpoints.method_for(endpoint_name)
166
+ endpoint_path = path or self._endpoints.path_for(endpoint_name)
167
+
168
+
169
+
170
+ if path_params:
171
+ endpoint_path = endpoint_path.format(**path_params)
172
+ response = self._client.request(endpoint_method, endpoint_path, **kwargs)
173
+
174
+ response.raise_for_status()
175
+ if error := response.json().get("Error"):
176
+ self._raise_error(error)
177
+
178
+ return response
179
+
180
+ def _raise_error(self, error: Mapping[str, Any]) -> None:
181
+ raise TicketClientError(error)
182
+
183
+ def close(self) -> None:
184
+ """
185
+ Closes the client connection
186
+ """
187
+ self._client.close()
188
+
189
+ def __enter__(self) -> "TicketClient":
190
+ return self
191
+
192
+ def __exit__(self, exc_type, exc, traceback) -> None:
193
+ self.close()
194
+
195
+ def _register_default_endpoints(self) -> None:
196
+ for name, (method, path) in _DEFAULT_ENDPOINTS.items():
197
+ if not self._endpoints.has(name):
198
+ self._endpoints.register(
199
+ Endpoint(name=name, method=method, path=path)
200
+ )
201
+
202
+
203
+ if __name__ == "__main__":
204
+ from dotenv import load_dotenv
205
+ load_dotenv()
206
+ import os
207
+
208
+ class_payload_create = TicketCreatePayload(
209
+ Ticket=TicketCreateTicket(
210
+ Title="Erro no login",
211
+ Queue="ITS::Ops-TechOps::Sentinelops::Cops React - N1",
212
+ State="new",
213
+ Priority="3 normal",
214
+ CustomerUser="joel.junior@eitisolucoes.com.br",
215
+ Type="Monitoramento",
216
+ ),
217
+ Article=TicketCreateArticle(
218
+ Subject="Não consigo acessar",
219
+ Body="Detalhes do problema...",
220
+ ContentType="text/plain; charset=utf-8",
221
+ Charset="utf-8",
222
+ MimeType="text/plain",
223
+ SenderType="customer",
224
+ From_="joel.junior@eitisolucoes.com.br",
225
+ ),
226
+ )
227
+
228
+ payload_create = {
229
+ "Ticket": {
230
+ "Title": "Erro no login",
231
+ "Queue": "ITS::Ops-TechOps::Sentinelops::Cops React - N1",
232
+ "State": "new",
233
+ "Priority": "3 normal",
234
+ "CustomerUser": "joel.junior@eitisolucoes.com.br",
235
+ "Type": "Monitoramento",
236
+ },
237
+ "Article": {
238
+ "Subject": "Não consigo acessar",
239
+ "Body": "Detalhes do problema...",
240
+ "ContentType": "text/plain; charset=utf-8",
241
+ "Charset": "utf-8",
242
+ "MimeType": "text/plain",
243
+ "SenderType": "customer",
244
+ "From": "joel.junior@eitisolucoes.com.br",
245
+ },
246
+ }
247
+
248
+ payload_update = {
249
+ "Ticket": {
250
+ "State": "open",
251
+ }
252
+ }
253
+
254
+
255
+ client = TicketClient(base_url=os.getenv("HOST"),
256
+ username=os.getenv("USER_LOGIN"), password=os.getenv("PASS"))
257
+
258
+ client.set_endpoint.ticket_get(endpoint="Tickets/{ticket_id}",
259
+ identifier="ticket_id")
260
+ response = client.ticket.get(ticket_id=5853276)
261
+ print("GET Ticket Response:", response.json())
@@ -0,0 +1,154 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ from .models import Endpoint
5
+ from typing import Iterable, Mapping, MutableMapping, TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from pyznuny.ticket.client import TicketClient
9
+
10
+ _DEFAULT_ENDPOINTS = {
11
+ "ticket_create": ("POST", "/Ticket"),
12
+ "ticket_update": ("PATCH", "/Ticket/{ticket_id}"),
13
+ "ticket_get": ("GET", "/Ticket/{ticket_id}"),
14
+ "session_create": ("POST", "/Session"),
15
+ }
16
+
17
+ _DEFAULT_ENDPOINT_IDENTIFIERS = {
18
+ "ticket_update": "ticket_id",
19
+ "ticket_get": "ticket_id",
20
+ }
21
+
22
+
23
+
24
+ class EndpointSetter:
25
+ """
26
+ Custom endpoint setter for the Ticket API
27
+
28
+ :arg client: Ticket client instance
29
+ :type client: TicketClient
30
+ """
31
+ def __init__(self, client: "TicketClient") -> None:
32
+ self._client = client
33
+
34
+
35
+ def ticket_create(self, *, endpoint: str, method: str = "POST") -> Endpoint:
36
+ """
37
+ Sets a custom endpoint for creating tickets
38
+
39
+ :param endpoint: Custom endpoint for creating tickets
40
+ :type endpoint: str
41
+ :param method: HTTP method for creating tickets, defaults to POST
42
+ :type method: str
43
+ :return: Endpoint object
44
+ :rtype: Endpoint
45
+ """
46
+ return self._client.register_endpoint("ticket_create", method, endpoint)
47
+
48
+ def ticket_get(
49
+ self,
50
+ *,
51
+ endpoint: str,
52
+ identifier: str = "ticket_id",
53
+ method: str = "GET",
54
+ ) -> Endpoint:
55
+ """
56
+ Sets a custom endpoint for retrieving a ticket.
57
+
58
+ :param endpoint: Custom endpoint path
59
+ :type endpoint: str
60
+ :param identifier: Identifier for the ticket ID in the endpoint path
61
+ :type identifier: str
62
+ :param method: HTTP method for the endpoint, defaults to GET
63
+ :type method: str
64
+ :return: Registered endpoint
65
+ :rtype: Endpoint
66
+ """
67
+ endpoint_obj = self._client.register_endpoint(
68
+ "ticket_get",
69
+ method,
70
+ endpoint,
71
+ )
72
+ self._client.set_endpoint_identifier("ticket_get", identifier)
73
+ return endpoint_obj
74
+
75
+ def ticket_update(
76
+ self,
77
+ *,
78
+ endpoint: str,
79
+ identifier: str = "ticket_id",
80
+ method: str = "POST",
81
+ ) -> Endpoint:
82
+ """
83
+ Sets a custom endpoint for updating a ticket.
84
+
85
+ :param endpoint: Custom endpoint path
86
+ :type endpoint: str
87
+ :param identifier: Identifier for the ticket ID in the endpoint path
88
+ :type identifier: str
89
+ :param method: HTTP method for the endpoint, defaults to POST
90
+ :type method: str
91
+ :return: Registered endpoint
92
+ :rtype: Endpoint
93
+ """
94
+ endpoint_obj = self._client.register_endpoint(
95
+ "ticket_update",
96
+ method,
97
+ endpoint,
98
+ )
99
+ self._client.set_endpoint_identifier("ticket_update", identifier)
100
+ return endpoint_obj
101
+
102
+
103
+
104
+
105
+ class EndpointsRegistry:
106
+ """
107
+ Object representing the registry of API endpoints for the Ticket API
108
+
109
+ :arg base_path: Base path for the endpoints, defaults to an empty string
110
+ :type base_path: str
111
+ """
112
+ def __init__(
113
+ self,
114
+ *,
115
+ base_path: str = "",
116
+ endpoints: Iterable[Endpoint] | None = None,
117
+ ) -> None:
118
+ self._base_path = base_path
119
+ self._endpoints: MutableMapping[str, Endpoint] = {}
120
+ if endpoints:
121
+ for endpoint in endpoints:
122
+ self.register(endpoint)
123
+
124
+ @property
125
+ def base_path(self) -> str:
126
+ return self._base_path
127
+
128
+ @base_path.setter
129
+ def base_path(self, value: str) -> None:
130
+ self._base_path = value
131
+
132
+ def register(self, endpoint: Endpoint) -> Endpoint:
133
+ self._endpoints[endpoint.name] = endpoint
134
+ return endpoint
135
+
136
+ def configure(self, mapping: Mapping[str, tuple[str, str]]) -> None:
137
+ for name, (method, path) in mapping.items():
138
+ self.register(Endpoint(name=name, method=method, path=path))
139
+
140
+ def get(self, name: str) -> Endpoint:
141
+ try:
142
+ return self._endpoints[name]
143
+ except KeyError as exc:
144
+ raise KeyError(f"Endpoint not registered: {name}") from exc
145
+
146
+ def has(self, name: str) -> bool:
147
+ return name in self._endpoints
148
+
149
+ def path_for(self, name: str) -> str:
150
+ endpoint = self.get(name)
151
+ return endpoint.full_path(self._base_path)
152
+
153
+ def method_for(self, name: str) -> str:
154
+ return self.get(name).method
@@ -0,0 +1,15 @@
1
+
2
+
3
+ from typing import Any, Mapping
4
+
5
+ class TicketClientError(Exception):
6
+ """
7
+ Docstring for TicketClientError
8
+ Represents an error returned by the TicketClient API.
9
+ """
10
+ def __init__(self, error: Mapping[str, Any] | str) -> None:
11
+
12
+ self.error = error
13
+ code = error.get("ErrorCode") if isinstance(error, dict) else error
14
+ message = error.get("ErrorMessage") or "Unknown error" if isinstance(error, dict) else str(error)
15
+ super().__init__(f"{code}: {message}" if code else str(message))
@@ -0,0 +1,172 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Mapping
5
+
6
+
7
+ _VALID_METHODS = {
8
+ "GET",
9
+ "POST",
10
+ "PUT",
11
+ "PATCH",
12
+ "DELETE",
13
+ "HEAD",
14
+ "OPTIONS",
15
+ }
16
+
17
+
18
+ def _clean_dict(data: Mapping[str, Any]) -> dict[str, Any]:
19
+ return {key: value for key, value in data.items() if value is not None}
20
+
21
+
22
+ def _require_non_empty(value: str, field: str) -> None:
23
+ if not value or not str(value).strip():
24
+ raise ValueError(f"{field} is required.")
25
+
26
+
27
+ def _normalize_method(method: str) -> str:
28
+ normalized = method.strip().upper()
29
+ if normalized not in _VALID_METHODS:
30
+ valid = ", ".join(sorted(_VALID_METHODS))
31
+ raise ValueError(f"Unsupported HTTP method: {method!r}. Use one of: {valid}")
32
+ return normalized
33
+
34
+
35
+ def _normalize_path(path: str) -> str:
36
+ normalized = "/" + path.lstrip("/")
37
+ if normalized == "/":
38
+ raise ValueError("Endpoint path cannot be empty.")
39
+ return normalized
40
+
41
+
42
+ def _join_base_path(base_path: str, endpoint_path: str) -> str:
43
+ base = base_path.strip("/")
44
+ tail = endpoint_path.lstrip("/")
45
+ if not base:
46
+ return "/" + tail
47
+ return f"/{base}/{tail}"
48
+
49
+ @dataclass(frozen=True, slots=True)
50
+ class Endpoint:
51
+ """
52
+ Object representing an API endpoint
53
+
54
+ :arg name: Name of the endpoint
55
+ :type name: str
56
+ :arg method: HTTP method for the endpoint
57
+ :type method: str
58
+ :arg path: Endpoint path
59
+ :type path: str
60
+ """
61
+ name: str
62
+ method: str
63
+ path: str
64
+
65
+ def __post_init__(self) -> None:
66
+ object.__setattr__(self, "method", _normalize_method(self.method))
67
+ object.__setattr__(self, "path", _normalize_path(self.path))
68
+
69
+ def full_path(self, base_path: str = "") -> str:
70
+ """
71
+ Returns the full path for the endpoint
72
+
73
+ :param base_path: Base path for the endpoint, defaults to an empty string
74
+ :type base_path: str
75
+ :return: Full path for the endpoint
76
+ :rtype: str
77
+ """
78
+ return _join_base_path(base_path, self.path)
79
+
80
+
81
+
82
+ @dataclass(slots=True)
83
+ class TicketCreateTicket:
84
+ Title: str
85
+ Queue: str
86
+ State: str
87
+ Priority: str
88
+ CustomerUser: str | None = None
89
+ Type: str | None = None
90
+ Service: str | None = None
91
+ SLA: str | None = None
92
+ Owner: str | None = None
93
+ Responsible: str | None = None
94
+
95
+ def validate(self) -> None:
96
+ _require_non_empty(self.Title, "Ticket.Title")
97
+ _require_non_empty(self.Queue, "Ticket.Queue")
98
+ _require_non_empty(self.State, "Ticket.State")
99
+ _require_non_empty(self.Priority, "Ticket.Priority")
100
+
101
+ def to_dict(self) -> dict[str, Any]:
102
+ self.validate()
103
+ return _clean_dict(
104
+ {
105
+ "Title": self.Title,
106
+ "Queue": self.Queue,
107
+ "State": self.State,
108
+ "Priority": self.Priority,
109
+ "CustomerUser": self.CustomerUser,
110
+ "Type": self.Type,
111
+ "Service": self.Service,
112
+ "SLA": self.SLA,
113
+ "Owner": self.Owner,
114
+ "Responsible": self.Responsible,
115
+ }
116
+ )
117
+
118
+
119
+ @dataclass(slots=True)
120
+ class TicketCreateArticle:
121
+ Subject: str
122
+ Body: str
123
+ ContentType: str
124
+ Charset: str | None = None
125
+ MimeType: str | None = None
126
+ SenderType: str | None = None
127
+ From_: str | None = None
128
+
129
+ def validate(self) -> None:
130
+ _require_non_empty(self.Subject, "Article.Subject")
131
+ _require_non_empty(self.Body, "Article.Body")
132
+ _require_non_empty(self.ContentType, "Article.ContentType")
133
+
134
+ def to_dict(self) -> dict[str, Any]:
135
+ self.validate()
136
+ return _clean_dict(
137
+ {
138
+ "Subject": self.Subject,
139
+ "Body": self.Body,
140
+ "ContentType": self.ContentType,
141
+ "Charset": self.Charset,
142
+ "MimeType": self.MimeType,
143
+ "SenderType": self.SenderType,
144
+ "From": self.From_,
145
+ }
146
+ )
147
+
148
+
149
+ @dataclass(slots=True)
150
+ class TicketCreatePayload:
151
+ Ticket: TicketCreateTicket
152
+ Article: TicketCreateArticle
153
+ DynamicField: Mapping[str, Any] | None = None
154
+ Attachment: list[Mapping[str, Any]] | None = None
155
+ TimeUnit: int | None = None
156
+
157
+ def to_dict(self) -> dict[str, Any]:
158
+ return _clean_dict(
159
+ {
160
+ "Ticket": self.Ticket.to_dict(),
161
+ "Article": self.Article.to_dict(),
162
+ "DynamicField": self.DynamicField,
163
+ "Attachment": self.Attachment,
164
+ "TimeUnit": self.TimeUnit,
165
+ }
166
+ )
167
+
168
+
169
+
170
+ class TicketUpdateTicket(TicketCreateTicket):
171
+ def validate(self):
172
+ pass
@@ -0,0 +1,78 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Mapping
4
+
5
+ import httpx
6
+
7
+ from pyznuny.ticket.models import TicketCreatePayload
8
+
9
+ if TYPE_CHECKING:
10
+ from pyznuny.ticket.client import TicketClient
11
+
12
+
13
+ class SessionRoutes:
14
+ def __init__(self, client: "TicketClient") -> None:
15
+ self._client = client
16
+
17
+ def create(self, username: str, password: str) -> httpx.Response:
18
+ """
19
+ Creates a new session with the given username and password.
20
+
21
+ :param username: username for authentication
22
+ :type username: str
23
+ :param password: password for authentication
24
+ :type password: str
25
+ :return: Response object containing session details
26
+ :rtype: Response
27
+ """
28
+ return self._client.request(
29
+ "session_create",
30
+ json={"UserLogin": username, "Password": password},
31
+ )
32
+
33
+
34
+ class TicketRoutes:
35
+ def __init__(self, client: "TicketClient") -> None:
36
+ self._client = client
37
+
38
+ def create(
39
+ self,
40
+ payload: TicketCreatePayload | Mapping[str, Any] | None = None,
41
+ **payload_kwargs: Any,
42
+ ) -> httpx.Response:
43
+
44
+ if payload is None:
45
+ payload_dict = dict(payload_kwargs)
46
+
47
+ elif isinstance(payload, TicketCreatePayload):
48
+ payload_dict = payload.to_dict()
49
+ payload_dict.update(payload_kwargs)
50
+ else:
51
+ payload_dict = dict(payload)
52
+ payload_dict.update(payload_kwargs)
53
+
54
+ payload_dict.update({"SessionID": self._client.session_id})
55
+ return self._client.request("ticket_create", json=payload_dict)
56
+
57
+
58
+ def update(self, ticket_id: str | int , **payload: dict) -> httpx.Response:
59
+ identifier = self._client.endpoint_identifier("ticket_update")
60
+
61
+ payload.update({"SessionID": self._client.session_id})
62
+ return self._client.request(
63
+ "ticket_update",
64
+ path_params={identifier: ticket_id},
65
+ json=payload,
66
+ )
67
+
68
+ def get(self, ticket_id: str | int,
69
+ dynamic_fields:int=0,
70
+ all_articles:int=0) -> httpx.Response:
71
+ identifier = self._client.endpoint_identifier("ticket_get")
72
+ return self._client.request(
73
+ "ticket_get",
74
+ path_params={identifier: ticket_id},
75
+ params={"SessionID": self._client.session_id,
76
+ "DynamicFields": dynamic_fields,
77
+ "AllArticles": all_articles},
78
+ )
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyznuny
3
+ Version: 0.0.1
4
+ Summary: A Python client for interacting with the Znuny ticketing system API.
5
+ Author-email: Junior Rosa <jr.dasrosas@gmail.com>, Pablo Gascon <pablogasconiel445@gmail.com>
6
+ Project-URL: Homepage, https://github.com/Junior-Rosa/py-znuny
7
+ Project-URL: Repository, https://github.com/Junior-Rosa/py-znuny
8
+ Project-URL: Issues, https://github.com/Junior-Rosa/py-znuny/issues
9
+ Requires-Python: >=3.14
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: httpx>=0.28.1
12
+ Requires-Dist: pydantic>=2.12.5
@@ -0,0 +1,11 @@
1
+ pyznuny/__init__.py,sha256=2hs3juXhAKpKyn_poQYzFr8ns0jmf_uBNKxT6u6w4Wo,68
2
+ pyznuny/ticket/__init__.py,sha256=lnWfZ1Al7LLLogUK_EUS_8WSNrRKzoJdbuYxpMXnUcA,215
3
+ pyznuny/ticket/client.py,sha256=kfYZIKFrCEJ7-A7aHF9ZMl_uw3tVj_1FAy3vrRw8JlI,8641
4
+ pyznuny/ticket/endpoints.py,sha256=nX5zJxOtwiwg3rtnt_7h4tRgSTyKPwof0i1sqqxljug,4444
5
+ pyznuny/ticket/exceptions.py,sha256=fqJFlMY1wl3Fb98Fz2odolOIyaDhKEwH1kP8Uv2k9-s,535
6
+ pyznuny/ticket/models.py,sha256=ezUxGc_Ftot0J2Entz5NXB7oXAeBIiuulN7NlxXTVTc,4692
7
+ pyznuny/ticket/routes.py,sha256=54cr08Z2NM9JwWCkY47E1Kj94Uen9d1PJXrJ24mb2nM,2514
8
+ pyznuny-0.0.1.dist-info/METADATA,sha256=xQPKpX-wpBmKYxvy5iFeyPLBaO-cNY-Z4xw2uD0pc_U,544
9
+ pyznuny-0.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
10
+ pyznuny-0.0.1.dist-info/top_level.txt,sha256=ki9uLRbo2oQCeiEaaYUcnYMj1mD_dkIhQroMCdJJXmk,8
11
+ pyznuny-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ pyznuny