glpi-python-client 0.4.3__py3-none-any.whl → 0.6.0__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 +45 -1
- glpi_python_client/_async/auth/_v1_session.py +3 -3
- glpi_python_client/_async/auth/auth.py +3 -3
- glpi_python_client/_async/clients/_base_client.py +1 -7
- glpi_python_client/_async/clients/api/__init__.py +13 -3
- glpi_python_client/_async/clients/api/assets/__init__.py +7 -0
- glpi_python_client/_async/clients/api/assets/_computer.py +427 -0
- glpi_python_client/_async/clients/api/dropdowns/__init__.py +4 -1
- glpi_python_client/_async/clients/api/dropdowns/_contract_type.py +220 -0
- glpi_python_client/_async/clients/api/management/__init__.py +2 -1
- glpi_python_client/_async/clients/api/management/_contract.py +411 -0
- glpi_python_client/_async/clients/client.py +7 -6
- glpi_python_client/_async/clients/commons/_constants.py +9 -1
- glpi_python_client/_async/clients/commons/_payloads.py +8 -6
- glpi_python_client/_errors.py +36 -4
- glpi_python_client/_sync/auth/_v1_session.py +3 -3
- glpi_python_client/_sync/auth/auth.py +3 -3
- glpi_python_client/_sync/clients/_base_client.py +1 -7
- glpi_python_client/_sync/clients/api/__init__.py +13 -3
- glpi_python_client/_sync/clients/api/assets/__init__.py +7 -0
- glpi_python_client/_sync/clients/api/assets/_computer.py +427 -0
- glpi_python_client/_sync/clients/api/dropdowns/__init__.py +4 -1
- glpi_python_client/_sync/clients/api/dropdowns/_contract_type.py +220 -0
- glpi_python_client/_sync/clients/api/management/__init__.py +2 -1
- glpi_python_client/_sync/clients/api/management/_contract.py +411 -0
- glpi_python_client/_sync/clients/client.py +7 -6
- glpi_python_client/_sync/clients/commons/_constants.py +9 -1
- glpi_python_client/_sync/clients/commons/_payloads.py +8 -6
- glpi_python_client/content/conversion.py +622 -77
- glpi_python_client/models/__init__.py +48 -3
- glpi_python_client/models/_base.py +135 -6
- glpi_python_client/models/api_schema/__init__.py +8 -1
- glpi_python_client/models/api_schema/_content.py +236 -28
- glpi_python_client/models/api_schema/assets/__init__.py +25 -0
- glpi_python_client/models/api_schema/assets/_computer.py +257 -0
- glpi_python_client/models/api_schema/assets/_contract_item.py +102 -0
- glpi_python_client/models/api_schema/assistance/_ticket.py +30 -9
- glpi_python_client/models/api_schema/assistance/timeline/_followup.py +29 -8
- glpi_python_client/models/api_schema/assistance/timeline/_solution.py +29 -8
- glpi_python_client/models/api_schema/assistance/timeline/_task.py +29 -8
- glpi_python_client/models/api_schema/dropdowns/__init__.py +10 -0
- glpi_python_client/models/api_schema/dropdowns/_contract_type.py +100 -0
- glpi_python_client/models/api_schema/enums.py +14 -0
- glpi_python_client/models/api_schema/knowledgebase/_article.py +49 -7
- glpi_python_client/models/api_schema/knowledgebase/_revision.py +28 -5
- glpi_python_client/models/api_schema/management/__init__.py +20 -0
- glpi_python_client/models/api_schema/management/_contract.py +328 -0
- glpi_python_client/models/api_schema/management/_contract_cost.py +139 -0
- glpi_python_client/models/custom_schema/_ticket_context.py +29 -6
- glpi_python_client/rsql.py +74 -14
- {glpi_python_client-0.4.3.dist-info → glpi_python_client-0.6.0.dist-info}/METADATA +14 -11
- {glpi_python_client-0.4.3.dist-info → glpi_python_client-0.6.0.dist-info}/RECORD +54 -40
- {glpi_python_client-0.4.3.dist-info → glpi_python_client-0.6.0.dist-info}/WHEEL +1 -1
- {glpi_python_client-0.4.3.dist-info → glpi_python_client-0.6.0.dist-info}/licenses/LICENSE +0 -0
glpi_python_client/__init__.py
CHANGED
|
@@ -20,6 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
from glpi_python_client._async.clients import AsyncGlpiClient
|
|
21
21
|
from glpi_python_client._errors import (
|
|
22
22
|
GlpiAuthError,
|
|
23
|
+
GlpiContentError,
|
|
23
24
|
GlpiError,
|
|
24
25
|
GlpiNotFoundError,
|
|
25
26
|
GlpiProtocolError,
|
|
@@ -31,6 +32,11 @@ from glpi_python_client._errors import (
|
|
|
31
32
|
)
|
|
32
33
|
from glpi_python_client._sync.clients import GlpiClient
|
|
33
34
|
from glpi_python_client.models import (
|
|
35
|
+
DeleteComputer,
|
|
36
|
+
DeleteContract,
|
|
37
|
+
DeleteContractCost,
|
|
38
|
+
DeleteContractItem,
|
|
39
|
+
DeleteContractType,
|
|
34
40
|
DeleteDocument,
|
|
35
41
|
DeleteEntity,
|
|
36
42
|
DeleteFollowup,
|
|
@@ -44,6 +50,11 @@ from glpi_python_client.models import (
|
|
|
44
50
|
DeleteTicketTask,
|
|
45
51
|
DeleteTimelineDocument,
|
|
46
52
|
DeleteUser,
|
|
53
|
+
GetComputer,
|
|
54
|
+
GetContract,
|
|
55
|
+
GetContractCost,
|
|
56
|
+
GetContractItem,
|
|
57
|
+
GetContractType,
|
|
47
58
|
GetDocument,
|
|
48
59
|
GetEntity,
|
|
49
60
|
GetFollowup,
|
|
@@ -61,6 +72,7 @@ from glpi_python_client.models import (
|
|
|
61
72
|
GetTicketTask,
|
|
62
73
|
GetTimelineDocument,
|
|
63
74
|
GetUser,
|
|
75
|
+
GlpiContractRenewalType,
|
|
64
76
|
GlpiEnum,
|
|
65
77
|
GlpiGlobalValidation,
|
|
66
78
|
GlpiPriority,
|
|
@@ -74,6 +86,11 @@ from glpi_python_client.models import (
|
|
|
74
86
|
IdNameCompletenameRef,
|
|
75
87
|
IdNameRef,
|
|
76
88
|
IdRef,
|
|
89
|
+
PatchComputer,
|
|
90
|
+
PatchContract,
|
|
91
|
+
PatchContractCost,
|
|
92
|
+
PatchContractItem,
|
|
93
|
+
PatchContractType,
|
|
77
94
|
PatchDocument,
|
|
78
95
|
PatchEntity,
|
|
79
96
|
PatchFollowup,
|
|
@@ -87,6 +104,11 @@ from glpi_python_client.models import (
|
|
|
87
104
|
PatchTicketTask,
|
|
88
105
|
PatchTimelineDocument,
|
|
89
106
|
PatchUser,
|
|
107
|
+
PostComputer,
|
|
108
|
+
PostContract,
|
|
109
|
+
PostContractCost,
|
|
110
|
+
PostContractItem,
|
|
111
|
+
PostContractType,
|
|
90
112
|
PostDocument,
|
|
91
113
|
PostEntity,
|
|
92
114
|
PostFollowup,
|
|
@@ -109,10 +131,15 @@ from glpi_python_client.rsql import (
|
|
|
109
131
|
date_window,
|
|
110
132
|
)
|
|
111
133
|
|
|
112
|
-
__version__ = "0.
|
|
134
|
+
__version__ = "0.6.0"
|
|
113
135
|
|
|
114
136
|
__all__ = [
|
|
115
137
|
"AsyncGlpiClient",
|
|
138
|
+
"DeleteComputer",
|
|
139
|
+
"DeleteContract",
|
|
140
|
+
"DeleteContractCost",
|
|
141
|
+
"DeleteContractItem",
|
|
142
|
+
"DeleteContractType",
|
|
116
143
|
"DeleteDocument",
|
|
117
144
|
"DeleteEntity",
|
|
118
145
|
"DeleteFollowup",
|
|
@@ -126,6 +153,11 @@ __all__ = [
|
|
|
126
153
|
"DeleteTicketTask",
|
|
127
154
|
"DeleteTimelineDocument",
|
|
128
155
|
"DeleteUser",
|
|
156
|
+
"GetComputer",
|
|
157
|
+
"GetContract",
|
|
158
|
+
"GetContractCost",
|
|
159
|
+
"GetContractItem",
|
|
160
|
+
"GetContractType",
|
|
129
161
|
"GetDocument",
|
|
130
162
|
"GetEntity",
|
|
131
163
|
"GetFollowup",
|
|
@@ -145,6 +177,8 @@ __all__ = [
|
|
|
145
177
|
"GetUser",
|
|
146
178
|
"GlpiAuthError",
|
|
147
179
|
"GlpiClient",
|
|
180
|
+
"GlpiContentError",
|
|
181
|
+
"GlpiContractRenewalType",
|
|
148
182
|
"GlpiEnum",
|
|
149
183
|
"GlpiError",
|
|
150
184
|
"GlpiGlobalValidation",
|
|
@@ -166,6 +200,11 @@ __all__ = [
|
|
|
166
200
|
"IdNameCompletenameRef",
|
|
167
201
|
"IdNameRef",
|
|
168
202
|
"IdRef",
|
|
203
|
+
"PatchComputer",
|
|
204
|
+
"PatchContract",
|
|
205
|
+
"PatchContractCost",
|
|
206
|
+
"PatchContractItem",
|
|
207
|
+
"PatchContractType",
|
|
169
208
|
"PatchDocument",
|
|
170
209
|
"PatchEntity",
|
|
171
210
|
"PatchFollowup",
|
|
@@ -179,6 +218,11 @@ __all__ = [
|
|
|
179
218
|
"PatchTicketTask",
|
|
180
219
|
"PatchTimelineDocument",
|
|
181
220
|
"PatchUser",
|
|
221
|
+
"PostComputer",
|
|
222
|
+
"PostContract",
|
|
223
|
+
"PostContractCost",
|
|
224
|
+
"PostContractItem",
|
|
225
|
+
"PostContractType",
|
|
182
226
|
"PostDocument",
|
|
183
227
|
"PostEntity",
|
|
184
228
|
"PostFollowup",
|
|
@@ -37,7 +37,7 @@ from __future__ import annotations
|
|
|
37
37
|
|
|
38
38
|
import json
|
|
39
39
|
import logging
|
|
40
|
-
from datetime import datetime, timedelta
|
|
40
|
+
from datetime import UTC, datetime, timedelta
|
|
41
41
|
from typing import Any, cast
|
|
42
42
|
|
|
43
43
|
import httpx
|
|
@@ -170,7 +170,7 @@ class GLPIV1Session:
|
|
|
170
170
|
raise GlpiProtocolError("GLPI v1 initSession returned no session_token")
|
|
171
171
|
|
|
172
172
|
self._session_token = str(token)
|
|
173
|
-
self._session_started_at = datetime.now(tz=
|
|
173
|
+
self._session_started_at = datetime.now(tz=UTC)
|
|
174
174
|
logger.info("GLPI v1 session initialised.")
|
|
175
175
|
|
|
176
176
|
async def _ensure_session(self) -> None:
|
|
@@ -196,7 +196,7 @@ class GLPIV1Session:
|
|
|
196
196
|
|
|
197
197
|
if self._session_started_at is None:
|
|
198
198
|
return True
|
|
199
|
-
return datetime.now(tz=
|
|
199
|
+
return datetime.now(tz=UTC) >= (
|
|
200
200
|
self._session_started_at + self._session_refresh_interval
|
|
201
201
|
)
|
|
202
202
|
|
|
@@ -8,7 +8,7 @@ share the same authenticated session state.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
|
-
from datetime import datetime, timedelta
|
|
11
|
+
from datetime import UTC, datetime, timedelta
|
|
12
12
|
|
|
13
13
|
import httpx
|
|
14
14
|
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed
|
|
@@ -241,7 +241,7 @@ class GLPITokenManager:
|
|
|
241
241
|
if refresh_token:
|
|
242
242
|
self.refresh_token = refresh_token
|
|
243
243
|
expires_in = int(str(token_data.get("expires_in") or 3600))
|
|
244
|
-
now = datetime.now(tz=
|
|
244
|
+
now = datetime.now(tz=UTC)
|
|
245
245
|
self.token_updated_at = now
|
|
246
246
|
self.token_expires_at = now + timedelta(seconds=expires_in)
|
|
247
247
|
logger.info("GLPI OAuth token %s successfully.", label)
|
|
@@ -399,7 +399,7 @@ class GLPITokenManager:
|
|
|
399
399
|
await self._acquire_token()
|
|
400
400
|
return
|
|
401
401
|
|
|
402
|
-
now = datetime.now(tz=
|
|
402
|
+
now = datetime.now(tz=UTC)
|
|
403
403
|
token_expired = (
|
|
404
404
|
self.token_expires_at is not None and now >= self.token_expires_at
|
|
405
405
|
)
|
|
@@ -13,13 +13,7 @@ from __future__ import annotations
|
|
|
13
13
|
|
|
14
14
|
import logging
|
|
15
15
|
import os
|
|
16
|
-
import
|
|
17
|
-
from typing import TYPE_CHECKING
|
|
18
|
-
|
|
19
|
-
if sys.version_info >= (3, 11):
|
|
20
|
-
from typing import Self
|
|
21
|
-
else: # pragma: no cover - fallback for Python 3.10
|
|
22
|
-
from typing_extensions import Self
|
|
16
|
+
from typing import TYPE_CHECKING, Self
|
|
23
17
|
|
|
24
18
|
from glpi_python_client._async._concurrency import Lock
|
|
25
19
|
from glpi_python_client._async.clients.commons._config import (
|
|
@@ -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/
|
|
4
|
+
``docs/api_contract/api.json`` one for one. They wrap the
|
|
5
5
|
transport helpers from :mod:`glpi_python_client._async.clients.commons` and exchange
|
|
6
6
|
typed ``Get<Name>``, ``Post<Name>``, ``Patch<Name>``, and ``Delete<Name>``
|
|
7
7
|
models with the GLPI API.
|
|
@@ -13,6 +13,7 @@ from glpi_python_client._async.clients.api.administration import (
|
|
|
13
13
|
EntityMixin,
|
|
14
14
|
UserMixin,
|
|
15
15
|
)
|
|
16
|
+
from glpi_python_client._async.clients.api.assets import ComputerMixin
|
|
16
17
|
from glpi_python_client._async.clients.api.assistance import (
|
|
17
18
|
TeamMemberMixin,
|
|
18
19
|
TicketMixin,
|
|
@@ -23,19 +24,28 @@ from glpi_python_client._async.clients.api.assistance.timeline import (
|
|
|
23
24
|
TicketTaskMixin,
|
|
24
25
|
TimelineDocumentMixin,
|
|
25
26
|
)
|
|
26
|
-
from glpi_python_client._async.clients.api.dropdowns import
|
|
27
|
+
from glpi_python_client._async.clients.api.dropdowns import (
|
|
28
|
+
ContractTypeMixin,
|
|
29
|
+
LocationMixin,
|
|
30
|
+
)
|
|
27
31
|
from glpi_python_client._async.clients.api.knowledgebase import (
|
|
28
32
|
KBArticleCommentMixin,
|
|
29
33
|
KBArticleMixin,
|
|
30
34
|
KBArticleRevisionMixin,
|
|
31
35
|
KBCategoryMixin,
|
|
32
36
|
)
|
|
33
|
-
from glpi_python_client._async.clients.api.management import
|
|
37
|
+
from glpi_python_client._async.clients.api.management import (
|
|
38
|
+
ContractMixin,
|
|
39
|
+
DocumentMixin,
|
|
40
|
+
)
|
|
34
41
|
from glpi_python_client._async.clients.api.plugins import (
|
|
35
42
|
PluginFieldsMixin,
|
|
36
43
|
)
|
|
37
44
|
|
|
38
45
|
__all__ = [
|
|
46
|
+
"ComputerMixin",
|
|
47
|
+
"ContractMixin",
|
|
48
|
+
"ContractTypeMixin",
|
|
39
49
|
"DocumentMixin",
|
|
40
50
|
"EntityMixin",
|
|
41
51
|
"FollowupMixin",
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
"""GLPI ``/Assets/Computer`` mixin.
|
|
2
|
+
|
|
3
|
+
The mixin exposes search, fetch, create, update, and delete helpers for the
|
|
4
|
+
GLPI computer resource using the contract-aligned ``api_schema`` models. It
|
|
5
|
+
also exposes CRUD helpers for the ``/Assets/Computer/{id}/Contract``
|
|
6
|
+
sub-resource, the join answering which contracts cover a given computer.
|
|
7
|
+
|
|
8
|
+
``link_computer_contract`` and ``update_computer_contract`` set ``itemtype``
|
|
9
|
+
and ``items_id`` themselves rather than trusting the caller-supplied values
|
|
10
|
+
on the request body; see ``models/api_schema/assets/_contract_item.py`` for
|
|
11
|
+
why.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from collections.abc import AsyncIterator
|
|
17
|
+
|
|
18
|
+
from glpi_python_client._async.clients.commons._constants import (
|
|
19
|
+
COMPUTER_ENDPOINT,
|
|
20
|
+
GlpiId,
|
|
21
|
+
)
|
|
22
|
+
from glpi_python_client._async.clients.commons._transport import TransportMixin
|
|
23
|
+
from glpi_python_client.models.api_schema.assets._computer import (
|
|
24
|
+
DeleteComputer,
|
|
25
|
+
GetComputer,
|
|
26
|
+
PatchComputer,
|
|
27
|
+
PostComputer,
|
|
28
|
+
)
|
|
29
|
+
from glpi_python_client.models.api_schema.assets._contract_item import (
|
|
30
|
+
DeleteContractItem,
|
|
31
|
+
GetContractItem,
|
|
32
|
+
PatchContractItem,
|
|
33
|
+
PostContractItem,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ComputerMixin(TransportMixin):
|
|
38
|
+
"""CRUD helpers for ``/Assets/Computer``."""
|
|
39
|
+
|
|
40
|
+
async def search_computers(
|
|
41
|
+
self,
|
|
42
|
+
rsql_filter: str = "",
|
|
43
|
+
*,
|
|
44
|
+
limit: int = 50,
|
|
45
|
+
start: int = 0,
|
|
46
|
+
sort: str | None = None,
|
|
47
|
+
) -> list[GetComputer]:
|
|
48
|
+
"""Search GLPI computers with an optional RSQL filter.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
rsql_filter : str, optional
|
|
53
|
+
Raw RSQL filter forwarded as the ``filter`` query parameter.
|
|
54
|
+
limit : int, optional
|
|
55
|
+
Maximum number of records returned by the GLPI server.
|
|
56
|
+
start : int, optional
|
|
57
|
+
Zero-based offset of the first record returned.
|
|
58
|
+
sort : str | None, optional
|
|
59
|
+
Server-side ordering expressed as ``"<field>:<direction>"``,
|
|
60
|
+
for example ``"date_mod:desc"``. Omitted when :data:`None`,
|
|
61
|
+
leaving the server default ordering in place.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
list[GetComputer]
|
|
66
|
+
Computers matching the filter.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
params: dict[str, object] = {"limit": limit, "start": start}
|
|
70
|
+
if rsql_filter:
|
|
71
|
+
params["filter"] = rsql_filter
|
|
72
|
+
if sort is not None:
|
|
73
|
+
params["sort"] = sort
|
|
74
|
+
return await self._resource_list(COMPUTER_ENDPOINT, GetComputer, params=params)
|
|
75
|
+
|
|
76
|
+
async def iter_search_computers(
|
|
77
|
+
self,
|
|
78
|
+
rsql_filter: str = "",
|
|
79
|
+
*,
|
|
80
|
+
batch_size: int = 50,
|
|
81
|
+
sort: str | None = None,
|
|
82
|
+
) -> AsyncIterator[list[GetComputer]]:
|
|
83
|
+
"""Yield successive pages of GLPI computers until exhausted.
|
|
84
|
+
|
|
85
|
+
The generator drives pagination automatically by advancing the
|
|
86
|
+
``start`` offset after each batch. Iteration stops when the server
|
|
87
|
+
returns fewer items than ``batch_size``, which signals the last page.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
rsql_filter : str, optional
|
|
92
|
+
Raw RSQL filter forwarded as the ``filter`` query parameter.
|
|
93
|
+
Empty by default, which lists every visible record.
|
|
94
|
+
batch_size : int, optional
|
|
95
|
+
Number of records requested per page (default 50). Acts as the
|
|
96
|
+
``limit`` parameter on each underlying :meth:`search_computers`
|
|
97
|
+
call.
|
|
98
|
+
sort : str | None, optional
|
|
99
|
+
Server-side ordering expressed as ``"<field>:<direction>"``,
|
|
100
|
+
for example ``"date_mod:desc"``. Forwarded to each page
|
|
101
|
+
request; omitted when :data:`None`, leaving the server default
|
|
102
|
+
ordering in place.
|
|
103
|
+
|
|
104
|
+
Yields
|
|
105
|
+
------
|
|
106
|
+
list[GetComputer]
|
|
107
|
+
One page per iteration. The last yielded batch may be shorter
|
|
108
|
+
than ``batch_size``.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
start = 0
|
|
112
|
+
while True:
|
|
113
|
+
batch = await self.search_computers(
|
|
114
|
+
rsql_filter,
|
|
115
|
+
limit=batch_size,
|
|
116
|
+
start=start,
|
|
117
|
+
sort=sort,
|
|
118
|
+
)
|
|
119
|
+
if batch:
|
|
120
|
+
yield batch
|
|
121
|
+
if len(batch) < batch_size:
|
|
122
|
+
break
|
|
123
|
+
start += batch_size
|
|
124
|
+
|
|
125
|
+
async def get_computer(self, computer_id: GlpiId) -> GetComputer:
|
|
126
|
+
"""Fetch one GLPI computer by identifier.
|
|
127
|
+
|
|
128
|
+
Parameters
|
|
129
|
+
----------
|
|
130
|
+
computer_id : GlpiId
|
|
131
|
+
Numeric identifier of the computer to retrieve.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
GetComputer
|
|
136
|
+
Validated computer payload.
|
|
137
|
+
|
|
138
|
+
Raises
|
|
139
|
+
------
|
|
140
|
+
GlpiStatusError
|
|
141
|
+
If the GLPI server returns a non-success HTTP status.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
return await self._resource_get(
|
|
145
|
+
f"{COMPUTER_ENDPOINT}/{computer_id}",
|
|
146
|
+
GetComputer,
|
|
147
|
+
failure_message=f"Failed to get computer {computer_id}",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
async def create_computer(self, computer: PostComputer) -> int:
|
|
151
|
+
"""Create one GLPI computer.
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
computer : PostComputer
|
|
156
|
+
Request body describing the computer to create.
|
|
157
|
+
|
|
158
|
+
Returns
|
|
159
|
+
-------
|
|
160
|
+
int
|
|
161
|
+
Identifier assigned by the GLPI server.
|
|
162
|
+
|
|
163
|
+
Raises
|
|
164
|
+
------
|
|
165
|
+
GlpiStatusError
|
|
166
|
+
If the GLPI server returns a non-success HTTP status.
|
|
167
|
+
GlpiProtocolError
|
|
168
|
+
If the create response is missing the ``id`` field.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
return await self._resource_create(
|
|
172
|
+
COMPUTER_ENDPOINT,
|
|
173
|
+
computer,
|
|
174
|
+
failure_message="Failed to create computer",
|
|
175
|
+
missing_message="GLPI computer create response did not include an ID",
|
|
176
|
+
log_message_factory=(lambda new_id: f"GLPI API created computer {new_id}"),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
async def update_computer(
|
|
180
|
+
self, computer_id: GlpiId, computer: PatchComputer
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Update one GLPI computer with a partial body.
|
|
183
|
+
|
|
184
|
+
Parameters
|
|
185
|
+
----------
|
|
186
|
+
computer_id : GlpiId
|
|
187
|
+
Numeric identifier of the computer to update.
|
|
188
|
+
computer : PatchComputer
|
|
189
|
+
Partial request body.
|
|
190
|
+
|
|
191
|
+
Returns
|
|
192
|
+
-------
|
|
193
|
+
None
|
|
194
|
+
|
|
195
|
+
Raises
|
|
196
|
+
------
|
|
197
|
+
GlpiStatusError
|
|
198
|
+
If the GLPI server returns a non-success HTTP status.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
await self._resource_update(
|
|
202
|
+
f"{COMPUTER_ENDPOINT}/{computer_id}",
|
|
203
|
+
computer,
|
|
204
|
+
failure_message=f"Failed to update computer {computer_id}",
|
|
205
|
+
log_message=f"GLPI API updated computer {computer_id}",
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
async def delete_computer(
|
|
209
|
+
self, computer_id: GlpiId, *, force: bool | None = None
|
|
210
|
+
) -> None:
|
|
211
|
+
"""Delete one GLPI computer by identifier.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
computer_id : GlpiId
|
|
216
|
+
Numeric identifier of the computer to delete.
|
|
217
|
+
force : bool | None, optional
|
|
218
|
+
When ``True`` the computer is permanently deleted instead of
|
|
219
|
+
being moved to the trash.
|
|
220
|
+
|
|
221
|
+
Returns
|
|
222
|
+
-------
|
|
223
|
+
None
|
|
224
|
+
|
|
225
|
+
Raises
|
|
226
|
+
------
|
|
227
|
+
GlpiStatusError
|
|
228
|
+
If the GLPI server returns a non-success HTTP status.
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
await self._resource_delete(
|
|
232
|
+
f"{COMPUTER_ENDPOINT}/{computer_id}",
|
|
233
|
+
failure_message=f"Failed to delete computer {computer_id}",
|
|
234
|
+
log_message=f"GLPI API deleted computer {computer_id}",
|
|
235
|
+
force=force,
|
|
236
|
+
delete_model_cls=DeleteComputer,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
async def list_computer_contracts(
|
|
240
|
+
self,
|
|
241
|
+
computer_id: GlpiId,
|
|
242
|
+
*,
|
|
243
|
+
limit: int = 50,
|
|
244
|
+
start: int = 0,
|
|
245
|
+
) -> list[GetContractItem]:
|
|
246
|
+
"""List the contracts linked to one GLPI computer.
|
|
247
|
+
|
|
248
|
+
Parameters
|
|
249
|
+
----------
|
|
250
|
+
computer_id : GlpiId
|
|
251
|
+
Numeric identifier of the owning computer.
|
|
252
|
+
limit : int, optional
|
|
253
|
+
Maximum number of records returned by the GLPI server.
|
|
254
|
+
start : int, optional
|
|
255
|
+
Zero-based offset of the first record returned.
|
|
256
|
+
|
|
257
|
+
Returns
|
|
258
|
+
-------
|
|
259
|
+
list[GetContractItem]
|
|
260
|
+
Contract links belonging to the computer.
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
params: dict[str, object] = {"limit": limit, "start": start}
|
|
264
|
+
return await self._resource_list(
|
|
265
|
+
f"{COMPUTER_ENDPOINT}/{computer_id}/Contract",
|
|
266
|
+
GetContractItem,
|
|
267
|
+
params=params,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
async def get_computer_contract(
|
|
271
|
+
self, computer_id: GlpiId, link_id: GlpiId
|
|
272
|
+
) -> GetContractItem:
|
|
273
|
+
"""Fetch one contract link recorded against a GLPI computer.
|
|
274
|
+
|
|
275
|
+
Parameters
|
|
276
|
+
----------
|
|
277
|
+
computer_id : GlpiId
|
|
278
|
+
Numeric identifier of the owning computer.
|
|
279
|
+
link_id : GlpiId
|
|
280
|
+
Numeric identifier of the contract link to retrieve.
|
|
281
|
+
|
|
282
|
+
Returns
|
|
283
|
+
-------
|
|
284
|
+
GetContractItem
|
|
285
|
+
Validated contract link payload.
|
|
286
|
+
|
|
287
|
+
Raises
|
|
288
|
+
------
|
|
289
|
+
GlpiStatusError
|
|
290
|
+
If the GLPI server returns a non-success HTTP status.
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
return await self._resource_get(
|
|
294
|
+
f"{COMPUTER_ENDPOINT}/{computer_id}/Contract/{link_id}",
|
|
295
|
+
GetContractItem,
|
|
296
|
+
failure_message=(
|
|
297
|
+
f"Failed to get contract link {link_id} for computer {computer_id}"
|
|
298
|
+
),
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
async def link_computer_contract(
|
|
302
|
+
self, computer_id: GlpiId, link: PostContractItem
|
|
303
|
+
) -> int:
|
|
304
|
+
"""Link one GLPI contract to one computer.
|
|
305
|
+
|
|
306
|
+
The ``itemtype`` and ``items_id`` fields are set from
|
|
307
|
+
``computer_id`` rather than taken from ``link``. The GLPI contract
|
|
308
|
+
types ``itemtype`` as a free string, so a caller-supplied value is
|
|
309
|
+
a silent mis-link waiting to happen; this helper already knows
|
|
310
|
+
which asset it is working on.
|
|
311
|
+
|
|
312
|
+
Parameters
|
|
313
|
+
----------
|
|
314
|
+
computer_id : GlpiId
|
|
315
|
+
Numeric identifier of the computer the contract covers.
|
|
316
|
+
link : PostContractItem
|
|
317
|
+
Request body naming the contract to link. Any ``itemtype`` or
|
|
318
|
+
``items_id`` set on it is replaced.
|
|
319
|
+
|
|
320
|
+
Returns
|
|
321
|
+
-------
|
|
322
|
+
int
|
|
323
|
+
Identifier assigned to the new link by the GLPI server.
|
|
324
|
+
|
|
325
|
+
Raises
|
|
326
|
+
------
|
|
327
|
+
GlpiStatusError
|
|
328
|
+
If the GLPI server returns a non-success HTTP status.
|
|
329
|
+
GlpiProtocolError
|
|
330
|
+
If the create response is missing the ``id`` field.
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
body = link.model_copy(update={"itemtype": "Computer", "items_id": computer_id})
|
|
334
|
+
return await self._resource_create(
|
|
335
|
+
f"{COMPUTER_ENDPOINT}/{computer_id}/Contract",
|
|
336
|
+
body,
|
|
337
|
+
failure_message=f"Failed to link a contract to computer {computer_id}",
|
|
338
|
+
missing_message=(
|
|
339
|
+
"GLPI contract link create response did not include an ID"
|
|
340
|
+
),
|
|
341
|
+
log_message_factory=(
|
|
342
|
+
lambda new_id: (
|
|
343
|
+
f"GLPI API linked contract item {new_id} to computer {computer_id}"
|
|
344
|
+
)
|
|
345
|
+
),
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
async def update_computer_contract(
|
|
349
|
+
self, computer_id: GlpiId, link_id: GlpiId, link: PatchContractItem
|
|
350
|
+
) -> None:
|
|
351
|
+
"""Update one computer-contract link with a partial body.
|
|
352
|
+
|
|
353
|
+
The ``itemtype`` and ``items_id`` fields are set from
|
|
354
|
+
``computer_id`` rather than taken from ``link``, for the same
|
|
355
|
+
reason as :meth:`link_computer_contract`.
|
|
356
|
+
|
|
357
|
+
Parameters
|
|
358
|
+
----------
|
|
359
|
+
computer_id : GlpiId
|
|
360
|
+
Numeric identifier of the owning computer.
|
|
361
|
+
link_id : GlpiId
|
|
362
|
+
Numeric identifier of the contract link to update.
|
|
363
|
+
link : PatchContractItem
|
|
364
|
+
Partial request body. Any ``itemtype`` or ``items_id`` set on
|
|
365
|
+
it is replaced.
|
|
366
|
+
|
|
367
|
+
Returns
|
|
368
|
+
-------
|
|
369
|
+
None
|
|
370
|
+
|
|
371
|
+
Raises
|
|
372
|
+
------
|
|
373
|
+
GlpiStatusError
|
|
374
|
+
If the GLPI server returns a non-success HTTP status.
|
|
375
|
+
"""
|
|
376
|
+
|
|
377
|
+
body = link.model_copy(update={"itemtype": "Computer", "items_id": computer_id})
|
|
378
|
+
await self._resource_update(
|
|
379
|
+
f"{COMPUTER_ENDPOINT}/{computer_id}/Contract/{link_id}",
|
|
380
|
+
body,
|
|
381
|
+
failure_message=(
|
|
382
|
+
f"Failed to update contract link {link_id} for computer {computer_id}"
|
|
383
|
+
),
|
|
384
|
+
log_message=(
|
|
385
|
+
f"GLPI API updated contract link {link_id} for computer {computer_id}"
|
|
386
|
+
),
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
async def unlink_computer_contract(
|
|
390
|
+
self, computer_id: GlpiId, link_id: GlpiId, *, force: bool | None = None
|
|
391
|
+
) -> None:
|
|
392
|
+
"""Remove one contract link from a GLPI computer.
|
|
393
|
+
|
|
394
|
+
Parameters
|
|
395
|
+
----------
|
|
396
|
+
computer_id : GlpiId
|
|
397
|
+
Numeric identifier of the owning computer.
|
|
398
|
+
link_id : GlpiId
|
|
399
|
+
Numeric identifier of the contract link to remove.
|
|
400
|
+
force : bool | None, optional
|
|
401
|
+
When ``True`` the link is permanently deleted instead of
|
|
402
|
+
being moved to the trash.
|
|
403
|
+
|
|
404
|
+
Returns
|
|
405
|
+
-------
|
|
406
|
+
None
|
|
407
|
+
|
|
408
|
+
Raises
|
|
409
|
+
------
|
|
410
|
+
GlpiStatusError
|
|
411
|
+
If the GLPI server returns a non-success HTTP status.
|
|
412
|
+
"""
|
|
413
|
+
|
|
414
|
+
await self._resource_delete(
|
|
415
|
+
f"{COMPUTER_ENDPOINT}/{computer_id}/Contract/{link_id}",
|
|
416
|
+
failure_message=(
|
|
417
|
+
f"Failed to unlink contract link {link_id} from computer {computer_id}"
|
|
418
|
+
),
|
|
419
|
+
log_message=(
|
|
420
|
+
f"GLPI API unlinked contract link {link_id} from computer {computer_id}"
|
|
421
|
+
),
|
|
422
|
+
force=force,
|
|
423
|
+
delete_model_cls=DeleteContractItem,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
__all__ = ["ComputerMixin"]
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from glpi_python_client._async.clients.api.dropdowns._contract_type import (
|
|
6
|
+
ContractTypeMixin,
|
|
7
|
+
)
|
|
5
8
|
from glpi_python_client._async.clients.api.dropdowns._location import LocationMixin
|
|
6
9
|
|
|
7
|
-
__all__ = ["LocationMixin"]
|
|
10
|
+
__all__ = ["ContractTypeMixin", "LocationMixin"]
|