glpi-python-client 0.2.1__py3-none-any.whl → 0.3.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.
- glpi_python_client/__init__.py +15 -5
- glpi_python_client/clients/__init__.py +13 -6
- glpi_python_client/clients/api/__init__.py +21 -21
- glpi_python_client/clients/api/administration/__init__.py +6 -5
- glpi_python_client/clients/api/administration/_entity.py +59 -17
- glpi_python_client/clients/api/administration/_user.py +63 -16
- glpi_python_client/clients/api/assistance/__init__.py +4 -4
- glpi_python_client/clients/api/assistance/_team.py +11 -13
- glpi_python_client/clients/api/assistance/_ticket.py +67 -17
- glpi_python_client/clients/api/assistance/timeline/__init__.py +9 -9
- glpi_python_client/clients/api/assistance/timeline/_document.py +16 -16
- glpi_python_client/clients/api/assistance/timeline/_followup.py +16 -18
- glpi_python_client/clients/api/assistance/timeline/_solution.py +16 -18
- glpi_python_client/clients/api/assistance/timeline/_task.py +16 -18
- glpi_python_client/clients/api/dropdowns/__init__.py +3 -3
- glpi_python_client/clients/api/dropdowns/_location.py +15 -17
- glpi_python_client/clients/api/management/__init__.py +3 -3
- glpi_python_client/clients/api/management/_document.py +25 -28
- glpi_python_client/clients/async_client.py +241 -0
- glpi_python_client/clients/commons/_async_bridge.py +174 -0
- glpi_python_client/clients/commons/_config.py +2 -1
- glpi_python_client/clients/commons/_transport.py +89 -61
- glpi_python_client/clients/commons/tests/test_transport.py +136 -0
- glpi_python_client/clients/custom/__init__.py +20 -3
- glpi_python_client/clients/custom/_statistics.py +442 -32
- glpi_python_client/clients/custom/_statistics_async.py +250 -0
- glpi_python_client/clients/custom/_ticket_context.py +17 -16
- glpi_python_client/clients/custom/_ticket_context_async.py +73 -0
- glpi_python_client/clients/custom/tests/test_statistics.py +382 -16
- glpi_python_client/clients/custom/tests/test_statistics_async.py +232 -0
- glpi_python_client/clients/custom/tests/test_ticket_context.py +57 -0
- glpi_python_client/clients/{glpi_client.py → sync_client.py} +55 -54
- glpi_python_client/clients/tests/test_api_coverage.py +262 -87
- glpi_python_client/clients/tests/test_async_branches.py +195 -0
- glpi_python_client/clients/tests/test_async_smoke.py +150 -0
- glpi_python_client/clients/tests/test_glpi_client.py +14 -14
- glpi_python_client/clients/tests/test_parity.py +70 -0
- glpi_python_client/clients/tests/test_smoke.py +28 -28
- glpi_python_client/models/__init__.py +5 -1
- glpi_python_client/models/api_schema/administration/_entity.py +44 -4
- glpi_python_client/models/api_schema/administration/_user.py +237 -12
- glpi_python_client/models/api_schema/administration/tests/test_administration_schemas.py +31 -0
- glpi_python_client/models/api_schema/assistance/_team.py +36 -11
- glpi_python_client/models/api_schema/assistance/_ticket.py +199 -10
- glpi_python_client/models/api_schema/assistance/timeline/_document.py +46 -4
- glpi_python_client/models/api_schema/assistance/timeline/_followup.py +92 -4
- glpi_python_client/models/api_schema/assistance/timeline/_solution.py +84 -4
- glpi_python_client/models/api_schema/assistance/timeline/_task.py +117 -4
- glpi_python_client/models/api_schema/dropdowns/_location.py +111 -4
- glpi_python_client/models/api_schema/management/_document.py +67 -4
- glpi_python_client/models/custom_schema/__init__.py +2 -1
- glpi_python_client/models/custom_schema/_ticket_context.py +143 -39
- glpi_python_client/models/custom_schema/tests/test_ticket_context.py +189 -1
- glpi_python_client/testing/fixtures.py +1 -1
- glpi_python_client/testing/utils.py +15 -2
- {glpi_python_client-0.2.1.dist-info → glpi_python_client-0.3.1.dist-info}/METADATA +58 -36
- {glpi_python_client-0.2.1.dist-info → glpi_python_client-0.3.1.dist-info}/RECORD +59 -49
- {glpi_python_client-0.2.1.dist-info → glpi_python_client-0.3.1.dist-info}/WHEEL +0 -0
- {glpi_python_client-0.2.1.dist-info → glpi_python_client-0.3.1.dist-info}/licenses/LICENSE +0 -0
glpi_python_client/__init__.py
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
"""Public import surface for the GLPI Python client package.
|
|
2
2
|
|
|
3
|
-
The package re-exports
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The package re-exports two client classes:
|
|
4
|
+
|
|
5
|
+
* :class:`GlpiClient` — synchronous, blocking client (single source of
|
|
6
|
+
truth for endpoint behaviour).
|
|
7
|
+
* :class:`AsyncGlpiClient` — asynchronous facade that wraps each
|
|
8
|
+
synchronous method into a coroutine.
|
|
9
|
+
|
|
10
|
+
The ``api_schema`` and ``custom_schema`` Pydantic models are also
|
|
11
|
+
re-exported so downstream users can import them from a single stable
|
|
12
|
+
package root.
|
|
6
13
|
"""
|
|
7
14
|
|
|
8
15
|
from __future__ import annotations
|
|
9
16
|
|
|
10
|
-
from glpi_python_client.clients import GlpiClient
|
|
17
|
+
from glpi_python_client.clients import AsyncGlpiClient, GlpiClient
|
|
11
18
|
from glpi_python_client.models import (
|
|
12
19
|
DeleteDocument,
|
|
13
20
|
DeleteEntity,
|
|
@@ -62,11 +69,13 @@ from glpi_python_client.models import (
|
|
|
62
69
|
PostTicketTask,
|
|
63
70
|
PostTimelineDocument,
|
|
64
71
|
PostUser,
|
|
72
|
+
TicketMarkdownOptions,
|
|
65
73
|
)
|
|
66
74
|
|
|
67
|
-
__version__ = "0.
|
|
75
|
+
__version__ = "0.3.1"
|
|
68
76
|
|
|
69
77
|
__all__ = [
|
|
78
|
+
"AsyncGlpiClient",
|
|
70
79
|
"DeleteDocument",
|
|
71
80
|
"DeleteEntity",
|
|
72
81
|
"DeleteFollowup",
|
|
@@ -121,5 +130,6 @@ __all__ = [
|
|
|
121
130
|
"PostTicketTask",
|
|
122
131
|
"PostTimelineDocument",
|
|
123
132
|
"PostUser",
|
|
133
|
+
"TicketMarkdownOptions",
|
|
124
134
|
"__version__",
|
|
125
135
|
]
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
"""Public client exports for the GLPI Python package.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
The package exposes two client classes:
|
|
4
|
+
|
|
5
|
+
* :class:`GlpiClient` — synchronous, blocking client. The single source
|
|
6
|
+
of truth for endpoint behaviour.
|
|
7
|
+
* :class:`AsyncGlpiClient` — asynchronous facade that wraps every
|
|
8
|
+
synchronous method into a coroutine via
|
|
9
|
+
:class:`~glpi_python_client.clients.commons._async_bridge.AsyncBridge`.
|
|
10
|
+
|
|
11
|
+
Both classes share the same endpoint surface; pick the one matching
|
|
12
|
+
your runtime model.
|
|
7
13
|
"""
|
|
8
14
|
|
|
9
15
|
from __future__ import annotations
|
|
10
16
|
|
|
11
|
-
from glpi_python_client.clients.
|
|
17
|
+
from glpi_python_client.clients.async_client import AsyncGlpiClient
|
|
18
|
+
from glpi_python_client.clients.sync_client import GlpiClient
|
|
12
19
|
|
|
13
|
-
__all__ = ["GlpiClient"]
|
|
20
|
+
__all__ = ["AsyncGlpiClient", "GlpiClient"]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Per-endpoint API mixins backed by the ``api_schema`` Pydantic models.
|
|
2
2
|
|
|
3
3
|
The mixins under this package mirror the endpoints documented in
|
|
4
|
-
``docs/glpi_api_contract.json`` one for one. They wrap the
|
|
4
|
+
``docs/glpi_api_contract.json`` one for one. They wrap the Synchronous
|
|
5
5
|
transport helpers from :mod:`glpi_python_client.clients.commons` and exchange
|
|
6
6
|
typed ``Get<Name>``, ``Post<Name>``, ``Patch<Name>``, and ``Delete<Name>``
|
|
7
7
|
models with the GLPI API.
|
|
@@ -10,31 +10,31 @@ models with the GLPI API.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
from glpi_python_client.clients.api.administration import (
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
EntityMixin,
|
|
14
|
+
UserMixin,
|
|
15
15
|
)
|
|
16
16
|
from glpi_python_client.clients.api.assistance import (
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
TeamMemberMixin,
|
|
18
|
+
TicketMixin,
|
|
19
19
|
)
|
|
20
20
|
from glpi_python_client.clients.api.assistance.timeline import (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
FollowupMixin,
|
|
22
|
+
SolutionMixin,
|
|
23
|
+
TicketTaskMixin,
|
|
24
|
+
TimelineDocumentMixin,
|
|
25
25
|
)
|
|
26
|
-
from glpi_python_client.clients.api.dropdowns import
|
|
27
|
-
from glpi_python_client.clients.api.management import
|
|
26
|
+
from glpi_python_client.clients.api.dropdowns import LocationMixin
|
|
27
|
+
from glpi_python_client.clients.api.management import DocumentMixin
|
|
28
28
|
|
|
29
29
|
__all__ = [
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
30
|
+
"DocumentMixin",
|
|
31
|
+
"EntityMixin",
|
|
32
|
+
"FollowupMixin",
|
|
33
|
+
"LocationMixin",
|
|
34
|
+
"SolutionMixin",
|
|
35
|
+
"TeamMemberMixin",
|
|
36
|
+
"TicketMixin",
|
|
37
|
+
"TicketTaskMixin",
|
|
38
|
+
"TimelineDocumentMixin",
|
|
39
|
+
"UserMixin",
|
|
40
40
|
]
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
"""GLPI ``/Administration`` mixins for the
|
|
1
|
+
"""GLPI ``/Administration`` mixins for the Synchronous client.
|
|
2
2
|
|
|
3
3
|
The submodules expose the user and entity mixins used by
|
|
4
|
-
:class:`glpi_python_client.clients.
|
|
4
|
+
:class:`glpi_python_client.clients.sync_client.GlpiClient` and
|
|
5
|
+
:class:`glpi_python_client.clients.async_client.AsyncGlpiClient`.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
from __future__ import annotations
|
|
8
9
|
|
|
9
|
-
from glpi_python_client.clients.api.administration._entity import
|
|
10
|
-
from glpi_python_client.clients.api.administration._user import
|
|
10
|
+
from glpi_python_client.clients.api.administration._entity import EntityMixin
|
|
11
|
+
from glpi_python_client.clients.api.administration._user import UserMixin
|
|
11
12
|
|
|
12
|
-
__all__ = ["
|
|
13
|
+
__all__ = ["EntityMixin", "UserMixin"]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Synchronous GLPI ``/Administration/Entity`` mixin.
|
|
2
2
|
|
|
3
3
|
The mixin exposes the search, fetch, create, update, and delete helpers
|
|
4
4
|
for the GLPI entity resource. Entity calls intentionally bypass the
|
|
@@ -7,8 +7,10 @@ client's ``GLPI-Entity`` header so cross-entity lookups remain possible.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
from collections.abc import Iterator
|
|
11
|
+
|
|
10
12
|
from glpi_python_client.clients.commons._constants import ENTITY_ENDPOINT, GlpiId
|
|
11
|
-
from glpi_python_client.clients.commons._transport import
|
|
13
|
+
from glpi_python_client.clients.commons._transport import TransportMixin
|
|
12
14
|
from glpi_python_client.models.api_schema.administration._entity import (
|
|
13
15
|
DeleteEntity,
|
|
14
16
|
GetEntity,
|
|
@@ -17,10 +19,10 @@ from glpi_python_client.models.api_schema.administration._entity import (
|
|
|
17
19
|
)
|
|
18
20
|
|
|
19
21
|
|
|
20
|
-
class
|
|
21
|
-
"""
|
|
22
|
+
class EntityMixin(TransportMixin):
|
|
23
|
+
"""Synchronous CRUD helpers for ``/Administration/Entity``."""
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
def search_entities(
|
|
24
26
|
self,
|
|
25
27
|
rsql_filter: str = "",
|
|
26
28
|
*,
|
|
@@ -50,11 +52,53 @@ class AsyncEntityMixin(AsyncTransportMixin):
|
|
|
50
52
|
params["limit"] = limit
|
|
51
53
|
if rsql_filter:
|
|
52
54
|
params["filter"] = rsql_filter
|
|
53
|
-
return
|
|
55
|
+
return self._resource_list(
|
|
54
56
|
ENTITY_ENDPOINT, GetEntity, params=params, skip_entity=True
|
|
55
57
|
)
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
def iter_search_entities(
|
|
60
|
+
self,
|
|
61
|
+
rsql_filter: str = "",
|
|
62
|
+
*,
|
|
63
|
+
batch_size: int = 50,
|
|
64
|
+
) -> Iterator[list[GetEntity]]:
|
|
65
|
+
"""Yield successive pages of GLPI entities until exhausted.
|
|
66
|
+
|
|
67
|
+
The generator drives pagination automatically by advancing the
|
|
68
|
+
``start`` offset after each batch. Iteration stops when the server
|
|
69
|
+
returns fewer items than ``batch_size``, which signals the last page.
|
|
70
|
+
Entity calls bypass the ``GLPI-Entity`` header so cross-entity
|
|
71
|
+
lookups remain possible.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
rsql_filter : str, optional
|
|
76
|
+
Raw RSQL filter forwarded as the ``filter`` query parameter.
|
|
77
|
+
Empty by default, which lists every accessible entity.
|
|
78
|
+
batch_size : int, optional
|
|
79
|
+
Number of records requested per page (default 50).
|
|
80
|
+
|
|
81
|
+
Yields
|
|
82
|
+
------
|
|
83
|
+
list[GetEntity]
|
|
84
|
+
One page of entities per iteration. The last yielded batch may
|
|
85
|
+
be shorter than ``batch_size``.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
start = 0
|
|
89
|
+
while True:
|
|
90
|
+
batch = self.search_entities(
|
|
91
|
+
rsql_filter,
|
|
92
|
+
limit=batch_size,
|
|
93
|
+
start=start,
|
|
94
|
+
)
|
|
95
|
+
if batch:
|
|
96
|
+
yield batch
|
|
97
|
+
if len(batch) < batch_size:
|
|
98
|
+
break
|
|
99
|
+
start += batch_size
|
|
100
|
+
|
|
101
|
+
def get_entity(self, entity_id: GlpiId) -> GetEntity:
|
|
58
102
|
"""Fetch one GLPI entity by identifier.
|
|
59
103
|
|
|
60
104
|
Parameters
|
|
@@ -73,14 +117,14 @@ class AsyncEntityMixin(AsyncTransportMixin):
|
|
|
73
117
|
If the GLPI server returns a non-success HTTP status.
|
|
74
118
|
"""
|
|
75
119
|
|
|
76
|
-
return
|
|
120
|
+
return self._resource_get(
|
|
77
121
|
f"{ENTITY_ENDPOINT}/{entity_id}",
|
|
78
122
|
GetEntity,
|
|
79
123
|
failure_message=f"Failed to get entity {entity_id}",
|
|
80
124
|
skip_entity=True,
|
|
81
125
|
)
|
|
82
126
|
|
|
83
|
-
|
|
127
|
+
def create_entity(self, entity: PostEntity) -> int:
|
|
84
128
|
"""Create one GLPI entity.
|
|
85
129
|
|
|
86
130
|
Parameters
|
|
@@ -100,7 +144,7 @@ class AsyncEntityMixin(AsyncTransportMixin):
|
|
|
100
144
|
non-success HTTP status.
|
|
101
145
|
"""
|
|
102
146
|
|
|
103
|
-
return
|
|
147
|
+
return self._resource_create(
|
|
104
148
|
ENTITY_ENDPOINT,
|
|
105
149
|
entity,
|
|
106
150
|
failure_message="Failed to create entity",
|
|
@@ -109,7 +153,7 @@ class AsyncEntityMixin(AsyncTransportMixin):
|
|
|
109
153
|
skip_entity=True,
|
|
110
154
|
)
|
|
111
155
|
|
|
112
|
-
|
|
156
|
+
def update_entity(self, entity_id: GlpiId, entity: PatchEntity) -> None:
|
|
113
157
|
"""Update one GLPI entity with a partial body.
|
|
114
158
|
|
|
115
159
|
Parameters
|
|
@@ -129,16 +173,14 @@ class AsyncEntityMixin(AsyncTransportMixin):
|
|
|
129
173
|
If the GLPI server returns a non-success HTTP status.
|
|
130
174
|
"""
|
|
131
175
|
|
|
132
|
-
|
|
176
|
+
self._resource_update(
|
|
133
177
|
f"{ENTITY_ENDPOINT}/{entity_id}",
|
|
134
178
|
entity,
|
|
135
179
|
failure_message=f"Failed to update entity {entity_id}",
|
|
136
180
|
log_message=f"GLPI API updated entity {entity_id}",
|
|
137
181
|
)
|
|
138
182
|
|
|
139
|
-
|
|
140
|
-
self, entity_id: GlpiId, *, force: bool | None = None
|
|
141
|
-
) -> None:
|
|
183
|
+
def delete_entity(self, entity_id: GlpiId, *, force: bool | None = None) -> None:
|
|
142
184
|
"""Delete one GLPI entity by identifier.
|
|
143
185
|
|
|
144
186
|
Parameters
|
|
@@ -159,7 +201,7 @@ class AsyncEntityMixin(AsyncTransportMixin):
|
|
|
159
201
|
If the GLPI server returns a non-success HTTP status.
|
|
160
202
|
"""
|
|
161
203
|
|
|
162
|
-
|
|
204
|
+
self._resource_delete(
|
|
163
205
|
f"{ENTITY_ENDPOINT}/{entity_id}",
|
|
164
206
|
failure_message=f"Failed to delete entity {entity_id}",
|
|
165
207
|
log_message=f"GLPI API deleted entity {entity_id}",
|
|
@@ -169,4 +211,4 @@ class AsyncEntityMixin(AsyncTransportMixin):
|
|
|
169
211
|
)
|
|
170
212
|
|
|
171
213
|
|
|
172
|
-
__all__ = ["
|
|
214
|
+
__all__ = ["EntityMixin"]
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Synchronous GLPI ``/Administration/User`` mixin.
|
|
2
2
|
|
|
3
3
|
The mixin exposes search, fetch, create, update, and delete helpers for the
|
|
4
4
|
GLPI user resource. All operations exchange the
|
|
5
5
|
:mod:`glpi_python_client.models.api_schema.administration` models and rely on
|
|
6
|
-
the
|
|
6
|
+
the Synchronous transport mixin for HTTP dispatch.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
+
from collections.abc import Iterator
|
|
12
|
+
|
|
11
13
|
from glpi_python_client.clients.commons._constants import USER_ENDPOINT, GlpiId
|
|
12
|
-
from glpi_python_client.clients.commons._transport import
|
|
14
|
+
from glpi_python_client.clients.commons._transport import TransportMixin
|
|
13
15
|
from glpi_python_client.models.api_schema.administration._user import (
|
|
14
16
|
DeleteUser,
|
|
15
17
|
GetUser,
|
|
@@ -18,15 +20,15 @@ from glpi_python_client.models.api_schema.administration._user import (
|
|
|
18
20
|
)
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
class
|
|
22
|
-
"""
|
|
23
|
+
class UserMixin(TransportMixin):
|
|
24
|
+
"""Synchronous CRUD helpers for ``/Administration/User``.
|
|
23
25
|
|
|
24
26
|
The helpers follow the contract-first naming convention and forward all
|
|
25
27
|
server-side validation to the GLPI API instead of duplicating checks on
|
|
26
28
|
the client side.
|
|
27
29
|
"""
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
def search_users(
|
|
30
32
|
self,
|
|
31
33
|
rsql_filter: str = "",
|
|
32
34
|
*,
|
|
@@ -59,11 +61,56 @@ class AsyncUserMixin(AsyncTransportMixin):
|
|
|
59
61
|
params: dict[str, object] = {"limit": limit, "start": start}
|
|
60
62
|
if rsql_filter:
|
|
61
63
|
params["filter"] = rsql_filter
|
|
62
|
-
return
|
|
64
|
+
return self._resource_list(
|
|
63
65
|
USER_ENDPOINT, GetUser, params=params, skip_entity=skip_entity
|
|
64
66
|
)
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
def iter_search_users(
|
|
69
|
+
self,
|
|
70
|
+
rsql_filter: str = "",
|
|
71
|
+
*,
|
|
72
|
+
batch_size: int = 50,
|
|
73
|
+
skip_entity: bool = False,
|
|
74
|
+
) -> Iterator[list[GetUser]]:
|
|
75
|
+
"""Yield successive pages of GLPI users until exhausted.
|
|
76
|
+
|
|
77
|
+
The generator drives pagination automatically by advancing the
|
|
78
|
+
``start`` offset after each batch. Iteration stops when the server
|
|
79
|
+
returns fewer items than ``batch_size``, which signals the last page.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
rsql_filter : str, optional
|
|
84
|
+
Raw RSQL filter forwarded as the ``filter`` query parameter.
|
|
85
|
+
Empty by default, which lists every visible user.
|
|
86
|
+
batch_size : int, optional
|
|
87
|
+
Number of records requested per page (default 50).
|
|
88
|
+
skip_entity : bool, optional
|
|
89
|
+
When ``True`` the ``GLPI-Entity`` header is omitted so the
|
|
90
|
+
search spans every entity the caller has access to.
|
|
91
|
+
|
|
92
|
+
Yields
|
|
93
|
+
------
|
|
94
|
+
list[GetUser]
|
|
95
|
+
One page of users per iteration. The last yielded batch may
|
|
96
|
+
be shorter than ``batch_size``.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
start = 0
|
|
100
|
+
while True:
|
|
101
|
+
batch = self.search_users(
|
|
102
|
+
rsql_filter,
|
|
103
|
+
limit=batch_size,
|
|
104
|
+
start=start,
|
|
105
|
+
skip_entity=skip_entity,
|
|
106
|
+
)
|
|
107
|
+
if batch:
|
|
108
|
+
yield batch
|
|
109
|
+
if len(batch) < batch_size:
|
|
110
|
+
break
|
|
111
|
+
start += batch_size
|
|
112
|
+
|
|
113
|
+
def get_user(self, user_id: GlpiId) -> GetUser:
|
|
67
114
|
"""Fetch one GLPI user by identifier.
|
|
68
115
|
|
|
69
116
|
Parameters
|
|
@@ -82,13 +129,13 @@ class AsyncUserMixin(AsyncTransportMixin):
|
|
|
82
129
|
If the GLPI server returns a non-success HTTP status.
|
|
83
130
|
"""
|
|
84
131
|
|
|
85
|
-
return
|
|
132
|
+
return self._resource_get(
|
|
86
133
|
f"{USER_ENDPOINT}/{user_id}",
|
|
87
134
|
GetUser,
|
|
88
135
|
failure_message=f"Failed to get user {user_id}",
|
|
89
136
|
)
|
|
90
137
|
|
|
91
|
-
|
|
138
|
+
def create_user(self, user: PostUser) -> int:
|
|
92
139
|
"""Create one GLPI user.
|
|
93
140
|
|
|
94
141
|
Parameters
|
|
@@ -108,7 +155,7 @@ class AsyncUserMixin(AsyncTransportMixin):
|
|
|
108
155
|
non-success HTTP status.
|
|
109
156
|
"""
|
|
110
157
|
|
|
111
|
-
return
|
|
158
|
+
return self._resource_create(
|
|
112
159
|
USER_ENDPOINT,
|
|
113
160
|
user,
|
|
114
161
|
failure_message="Failed to create user",
|
|
@@ -116,7 +163,7 @@ class AsyncUserMixin(AsyncTransportMixin):
|
|
|
116
163
|
log_message_factory=lambda new_id: f"GLPI API created user {new_id}",
|
|
117
164
|
)
|
|
118
165
|
|
|
119
|
-
|
|
166
|
+
def update_user(self, user_id: GlpiId, user: PatchUser) -> None:
|
|
120
167
|
"""Update one GLPI user with a partial body.
|
|
121
168
|
|
|
122
169
|
Parameters
|
|
@@ -136,14 +183,14 @@ class AsyncUserMixin(AsyncTransportMixin):
|
|
|
136
183
|
If the GLPI server returns a non-success HTTP status.
|
|
137
184
|
"""
|
|
138
185
|
|
|
139
|
-
|
|
186
|
+
self._resource_update(
|
|
140
187
|
f"{USER_ENDPOINT}/{user_id}",
|
|
141
188
|
user,
|
|
142
189
|
failure_message=f"Failed to update user {user_id}",
|
|
143
190
|
log_message=f"GLPI API updated user {user_id}",
|
|
144
191
|
)
|
|
145
192
|
|
|
146
|
-
|
|
193
|
+
def delete_user(self, user_id: GlpiId, *, force: bool | None = None) -> None:
|
|
147
194
|
"""Delete one GLPI user by identifier.
|
|
148
195
|
|
|
149
196
|
Parameters
|
|
@@ -164,7 +211,7 @@ class AsyncUserMixin(AsyncTransportMixin):
|
|
|
164
211
|
If the GLPI server returns a non-success HTTP status.
|
|
165
212
|
"""
|
|
166
213
|
|
|
167
|
-
|
|
214
|
+
self._resource_delete(
|
|
168
215
|
f"{USER_ENDPOINT}/{user_id}",
|
|
169
216
|
failure_message=f"Failed to delete user {user_id}",
|
|
170
217
|
log_message=f"GLPI API deleted user {user_id}",
|
|
@@ -173,4 +220,4 @@ class AsyncUserMixin(AsyncTransportMixin):
|
|
|
173
220
|
)
|
|
174
221
|
|
|
175
222
|
|
|
176
|
-
__all__ = ["
|
|
223
|
+
__all__ = ["UserMixin"]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
"""GLPI ``/Assistance`` mixins for the
|
|
1
|
+
"""GLPI ``/Assistance`` mixins for the Synchronous client."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from glpi_python_client.clients.api.assistance._team import
|
|
6
|
-
from glpi_python_client.clients.api.assistance._ticket import
|
|
5
|
+
from glpi_python_client.clients.api.assistance._team import TeamMemberMixin
|
|
6
|
+
from glpi_python_client.clients.api.assistance._ticket import TicketMixin
|
|
7
7
|
|
|
8
|
-
__all__ = ["
|
|
8
|
+
__all__ = ["TeamMemberMixin", "TicketMixin"]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Synchronous GLPI ``/Assistance/Ticket/{id}/TeamMember`` mixin.
|
|
2
2
|
|
|
3
3
|
The team-member endpoint exposes list, add, and remove operations on a
|
|
4
4
|
ticket. The mixin uses the ``api_schema`` ``TeamMember`` models and lets
|
|
@@ -16,7 +16,7 @@ from glpi_python_client.clients.commons._constants import (
|
|
|
16
16
|
)
|
|
17
17
|
from glpi_python_client.clients.commons._http import ensure_response_status
|
|
18
18
|
from glpi_python_client.clients.commons._payloads import model_to_payload
|
|
19
|
-
from glpi_python_client.clients.commons._transport import
|
|
19
|
+
from glpi_python_client.clients.commons._transport import TransportMixin
|
|
20
20
|
from glpi_python_client.models.api_schema.assistance._team import (
|
|
21
21
|
GetTeamMember,
|
|
22
22
|
PostTeamMember,
|
|
@@ -25,10 +25,10 @@ from glpi_python_client.models.api_schema.assistance._team import (
|
|
|
25
25
|
logger = logging.getLogger(__name__)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
class
|
|
29
|
-
"""
|
|
28
|
+
class TeamMemberMixin(TransportMixin):
|
|
29
|
+
"""Synchronous helpers for the ticket team-member endpoint."""
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
def list_ticket_team_members(self, ticket_id: GlpiId) -> list[GetTeamMember]:
|
|
32
32
|
"""List the team members currently linked to one ticket.
|
|
33
33
|
|
|
34
34
|
Parameters
|
|
@@ -47,15 +47,13 @@ class AsyncTeamMemberMixin(AsyncTransportMixin):
|
|
|
47
47
|
If the GLPI server returns a non-success HTTP status.
|
|
48
48
|
"""
|
|
49
49
|
|
|
50
|
-
return
|
|
50
|
+
return self._resource_list(
|
|
51
51
|
f"{TICKET_ENDPOINT}/{ticket_id}/{TEAM_MEMBER_SUFFIX}",
|
|
52
52
|
GetTeamMember,
|
|
53
53
|
failure_message=f"Failed to list ticket team members for {ticket_id}",
|
|
54
54
|
)
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
self, ticket_id: GlpiId, member: PostTeamMember
|
|
58
|
-
) -> None:
|
|
56
|
+
def add_ticket_team_member(self, ticket_id: GlpiId, member: PostTeamMember) -> None:
|
|
59
57
|
"""Add one team member to a ticket.
|
|
60
58
|
|
|
61
59
|
Parameters
|
|
@@ -79,7 +77,7 @@ class AsyncTeamMemberMixin(AsyncTransportMixin):
|
|
|
79
77
|
"""
|
|
80
78
|
|
|
81
79
|
endpoint = f"{TICKET_ENDPOINT}/{ticket_id}/{TEAM_MEMBER_SUFFIX}"
|
|
82
|
-
response =
|
|
80
|
+
response = self._post_request(endpoint, model_to_payload(member))
|
|
83
81
|
ensure_response_status(
|
|
84
82
|
response,
|
|
85
83
|
success_statuses=(200, 201),
|
|
@@ -87,7 +85,7 @@ class AsyncTeamMemberMixin(AsyncTransportMixin):
|
|
|
87
85
|
)
|
|
88
86
|
logger.info("GLPI API added team member on ticket %s", ticket_id)
|
|
89
87
|
|
|
90
|
-
|
|
88
|
+
def remove_ticket_team_member(
|
|
91
89
|
self, ticket_id: GlpiId, member: PostTeamMember
|
|
92
90
|
) -> None:
|
|
93
91
|
"""Remove one team member from a ticket.
|
|
@@ -110,7 +108,7 @@ class AsyncTeamMemberMixin(AsyncTransportMixin):
|
|
|
110
108
|
If the GLPI server returns a non-success HTTP status.
|
|
111
109
|
"""
|
|
112
110
|
|
|
113
|
-
|
|
111
|
+
self._resource_delete(
|
|
114
112
|
f"{TICKET_ENDPOINT}/{ticket_id}/{TEAM_MEMBER_SUFFIX}",
|
|
115
113
|
failure_message=f"Failed to remove team member on ticket {ticket_id}",
|
|
116
114
|
log_message=f"GLPI API removed team member on ticket {ticket_id}",
|
|
@@ -118,4 +116,4 @@ class AsyncTeamMemberMixin(AsyncTransportMixin):
|
|
|
118
116
|
)
|
|
119
117
|
|
|
120
118
|
|
|
121
|
-
__all__ = ["
|
|
119
|
+
__all__ = ["TeamMemberMixin"]
|