glpi-python-client 0.1.3__py3-none-any.whl → 0.2.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 +111 -22
- glpi_python_client/auth/__init__.py +5 -3
- glpi_python_client/{clients/api_v1_session.py → auth/_v1_session.py} +47 -216
- glpi_python_client/auth/tests/test_v1_session.py +283 -0
- glpi_python_client/clients/__init__.py +7 -12
- glpi_python_client/clients/api/__init__.py +40 -0
- glpi_python_client/clients/api/administration/__init__.py +12 -0
- glpi_python_client/clients/api/administration/_entity.py +172 -0
- glpi_python_client/clients/api/administration/_user.py +176 -0
- glpi_python_client/clients/api/assistance/__init__.py +8 -0
- glpi_python_client/clients/api/assistance/_team.py +121 -0
- glpi_python_client/clients/api/assistance/_ticket.py +185 -0
- glpi_python_client/clients/api/assistance/timeline/__init__.py +23 -0
- glpi_python_client/clients/api/assistance/timeline/_document.py +221 -0
- glpi_python_client/clients/api/assistance/timeline/_followup.py +203 -0
- glpi_python_client/clients/api/assistance/timeline/_solution.py +200 -0
- glpi_python_client/clients/api/assistance/timeline/_task.py +193 -0
- glpi_python_client/clients/api/dropdowns/__init__.py +7 -0
- glpi_python_client/clients/api/dropdowns/_location.py +166 -0
- glpi_python_client/clients/api/management/__init__.py +7 -0
- glpi_python_client/clients/api/management/_document.py +286 -0
- glpi_python_client/clients/commons/__init__.py +9 -0
- glpi_python_client/clients/{v2/common/client_config.py → commons/_config.py} +43 -29
- glpi_python_client/clients/commons/_constants.py +50 -0
- glpi_python_client/clients/{v2/common/errors.py → commons/_errors.py} +7 -3
- glpi_python_client/clients/commons/_filters.py +94 -0
- glpi_python_client/clients/{v2/common/request_http.py → commons/_http.py} +88 -43
- glpi_python_client/clients/commons/_payloads.py +42 -0
- glpi_python_client/clients/commons/_transport.py +494 -0
- glpi_python_client/clients/commons/tests/__init__.py +5 -0
- glpi_python_client/clients/commons/tests/test_errors.py +41 -0
- glpi_python_client/clients/commons/tests/test_filters.py +49 -0
- glpi_python_client/clients/commons/tests/test_http.py +131 -0
- glpi_python_client/clients/commons/tests/test_payloads.py +64 -0
- glpi_python_client/clients/custom/__init__.py +15 -0
- glpi_python_client/clients/custom/_statistics.py +264 -0
- glpi_python_client/clients/custom/_ticket_context.py +66 -0
- glpi_python_client/clients/custom/tests/test_statistics.py +196 -0
- glpi_python_client/clients/glpi_client.py +262 -0
- glpi_python_client/clients/tests/__init__.py +1 -5
- glpi_python_client/clients/tests/test_api_coverage.py +707 -0
- glpi_python_client/clients/tests/test_glpi_client.py +266 -0
- glpi_python_client/clients/tests/test_smoke.py +259 -0
- glpi_python_client/models/__init__.py +129 -20
- glpi_python_client/models/_base.py +43 -4
- glpi_python_client/models/api_schema/__init__.py +16 -0
- glpi_python_client/models/api_schema/_common.py +67 -0
- glpi_python_client/models/api_schema/_content.py +74 -0
- glpi_python_client/models/api_schema/administration/__init__.py +30 -0
- glpi_python_client/models/api_schema/administration/_entity.py +55 -0
- glpi_python_client/models/api_schema/administration/_user.py +132 -0
- glpi_python_client/models/api_schema/administration/tests/__init__.py +1 -0
- glpi_python_client/models/api_schema/administration/tests/test_administration_schemas.py +114 -0
- glpi_python_client/models/api_schema/assistance/__init__.py +30 -0
- glpi_python_client/models/api_schema/assistance/_team.py +59 -0
- glpi_python_client/models/api_schema/assistance/_ticket.py +156 -0
- glpi_python_client/models/api_schema/assistance/tests/__init__.py +1 -0
- glpi_python_client/models/api_schema/assistance/tests/test_assistance_schemas.py +108 -0
- glpi_python_client/models/api_schema/assistance/tests/test_content_roundtrip.py +102 -0
- glpi_python_client/models/api_schema/assistance/timeline/__init__.py +49 -0
- glpi_python_client/models/api_schema/assistance/timeline/_document.py +60 -0
- glpi_python_client/models/api_schema/assistance/timeline/_followup.py +78 -0
- glpi_python_client/models/api_schema/assistance/timeline/_solution.py +76 -0
- glpi_python_client/models/api_schema/assistance/timeline/_task.py +97 -0
- glpi_python_client/models/api_schema/assistance/timeline/tests/__init__.py +1 -0
- glpi_python_client/models/api_schema/assistance/timeline/tests/test_timeline_schemas.py +147 -0
- glpi_python_client/models/api_schema/dropdowns/__init__.py +15 -0
- glpi_python_client/models/api_schema/dropdowns/_location.py +86 -0
- glpi_python_client/models/api_schema/dropdowns/tests/__init__.py +1 -0
- glpi_python_client/models/api_schema/dropdowns/tests/test_dropdowns_schemas.py +61 -0
- glpi_python_client/models/api_schema/enums.py +156 -0
- glpi_python_client/models/api_schema/management/__init__.py +15 -0
- glpi_python_client/models/api_schema/management/_document.py +67 -0
- glpi_python_client/models/api_schema/management/tests/__init__.py +1 -0
- glpi_python_client/models/api_schema/management/tests/test_management_schemas.py +50 -0
- glpi_python_client/models/custom_schema/__init__.py +13 -0
- glpi_python_client/models/custom_schema/_ticket_context.py +239 -0
- glpi_python_client/models/custom_schema/tests/__init__.py +1 -0
- glpi_python_client/models/custom_schema/tests/test_ticket_context.py +210 -0
- glpi_python_client/testing/__init__.py +0 -4
- glpi_python_client/testing/fixtures.py +6 -32
- glpi_python_client/testing/utils.py +1 -42
- {glpi_python_client-0.1.3.dist-info → glpi_python_client-0.2.1.dist-info}/METADATA +61 -34
- glpi_python_client-0.2.1.dist-info/RECORD +93 -0
- glpi_python_client/clients/api_v2_client.py +0 -317
- glpi_python_client/clients/async_api_v2_client.py +0 -236
- glpi_python_client/clients/tests/test_api_v1_session.py +0 -85
- glpi_python_client/clients/tests/test_api_v2_client.py +0 -349
- glpi_python_client/clients/tests/test_async_api_v2_client.py +0 -257
- glpi_python_client/clients/v2/__init__.py +0 -8
- glpi_python_client/clients/v2/async_/__init__.py +0 -12
- glpi_python_client/clients/v2/async_/api.py +0 -29
- glpi_python_client/clients/v2/async_/directory.py +0 -88
- glpi_python_client/clients/v2/async_/documents.py +0 -144
- glpi_python_client/clients/v2/async_/team.py +0 -125
- glpi_python_client/clients/v2/async_/tests/__init__.py +0 -5
- glpi_python_client/clients/v2/async_/tests/test_directory.py +0 -43
- glpi_python_client/clients/v2/async_/tests/test_documents.py +0 -41
- glpi_python_client/clients/v2/async_/tests/test_team.py +0 -44
- glpi_python_client/clients/v2/async_/tests/test_tickets.py +0 -174
- glpi_python_client/clients/v2/async_/tests/test_timeline.py +0 -126
- glpi_python_client/clients/v2/async_/tickets.py +0 -312
- glpi_python_client/clients/v2/async_/timeline.py +0 -312
- glpi_python_client/clients/v2/async_/transport.py +0 -251
- glpi_python_client/clients/v2/common/__init__.py +0 -6
- glpi_python_client/clients/v2/common/constants.py +0 -45
- glpi_python_client/clients/v2/common/filters.py +0 -30
- glpi_python_client/clients/v2/common/payloads.py +0 -57
- glpi_python_client/clients/v2/common/response_payloads.py +0 -76
- glpi_python_client/clients/v2/common/ticket_search.py +0 -113
- glpi_python_client/clients/v2/sync/__init__.py +0 -12
- glpi_python_client/clients/v2/sync/api.py +0 -29
- glpi_python_client/clients/v2/sync/directory.py +0 -90
- glpi_python_client/clients/v2/sync/documents.py +0 -144
- glpi_python_client/clients/v2/sync/team.py +0 -125
- glpi_python_client/clients/v2/sync/tests/__init__.py +0 -5
- glpi_python_client/clients/v2/sync/tests/test_directory.py +0 -57
- glpi_python_client/clients/v2/sync/tests/test_documents.py +0 -99
- glpi_python_client/clients/v2/sync/tests/test_team.py +0 -64
- glpi_python_client/clients/v2/sync/tests/test_tickets.py +0 -430
- glpi_python_client/clients/v2/sync/tests/test_timeline.py +0 -77
- glpi_python_client/clients/v2/sync/tests/test_transport.py +0 -89
- glpi_python_client/clients/v2/sync/tickets.py +0 -312
- glpi_python_client/clients/v2/sync/timeline.py +0 -308
- glpi_python_client/clients/v2/sync/transport.py +0 -246
- glpi_python_client/content/records/__init__.py +0 -84
- glpi_python_client/content/records/core/__init__.py +0 -6
- glpi_python_client/content/records/core/document_links.py +0 -100
- glpi_python_client/content/records/core/normalization.py +0 -53
- glpi_python_client/content/records/core/references.py +0 -98
- glpi_python_client/content/records/core/scalars.py +0 -83
- glpi_python_client/content/records/parsers/__init__.py +0 -6
- glpi_python_client/content/records/parsers/directory.py +0 -62
- glpi_python_client/content/records/parsers/documents.py +0 -49
- glpi_python_client/content/records/parsers/team.py +0 -58
- glpi_python_client/content/records/parsers/tests/__init__.py +0 -5
- glpi_python_client/content/records/parsers/tests/test_tickets.py +0 -35
- glpi_python_client/content/records/parsers/tests/test_timeline.py +0 -20
- glpi_python_client/content/records/parsers/tickets.py +0 -96
- glpi_python_client/content/records/parsers/timeline.py +0 -119
- glpi_python_client/models/_payload.py +0 -79
- glpi_python_client/models/_shared.py +0 -37
- glpi_python_client/models/glpi/__init__.py +0 -27
- glpi_python_client/models/glpi/_document.py +0 -59
- glpi_python_client/models/glpi/_followup.py +0 -77
- glpi_python_client/models/glpi/_location.py +0 -53
- glpi_python_client/models/glpi/_solution.py +0 -57
- glpi_python_client/models/glpi/_task.py +0 -41
- glpi_python_client/models/glpi/_team_member.py +0 -33
- glpi_python_client/models/glpi/_ticket.py +0 -303
- glpi_python_client/models/glpi/_user.py +0 -92
- glpi_python_client/models/glpi/tests/__init__.py +0 -5
- glpi_python_client/models/glpi/tests/test__document.py +0 -12
- glpi_python_client/models/glpi/tests/test__followup.py +0 -31
- glpi_python_client/models/glpi/tests/test__location.py +0 -12
- glpi_python_client/models/glpi/tests/test__solution.py +0 -11
- glpi_python_client/models/glpi/tests/test__ticket.py +0 -59
- glpi_python_client/models/glpi/tests/test__user.py +0 -29
- glpi_python_client-0.1.3.dist-info/RECORD +0 -98
- {glpi_python_client-0.1.3.dist-info → glpi_python_client-0.2.1.dist-info}/WHEEL +0 -0
- {glpi_python_client-0.1.3.dist-info → glpi_python_client-0.2.1.dist-info}/licenses/LICENSE +0 -0
glpi_python_client/__init__.py
CHANGED
|
@@ -1,36 +1,125 @@
|
|
|
1
1
|
"""Public import surface for the GLPI Python client package.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
The package re-exports the asynchronous :class:`GlpiClient` and the
|
|
4
|
+
``api_schema`` and ``custom_schema`` Pydantic models so downstream users
|
|
5
|
+
can import them from a single stable package root.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
from __future__ import annotations
|
|
8
9
|
|
|
9
|
-
from glpi_python_client.clients import
|
|
10
|
+
from glpi_python_client.clients import GlpiClient
|
|
10
11
|
from glpi_python_client.models import (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
DeleteDocument,
|
|
13
|
+
DeleteEntity,
|
|
14
|
+
DeleteFollowup,
|
|
15
|
+
DeleteLocation,
|
|
16
|
+
DeleteSolution,
|
|
17
|
+
DeleteTeamMember,
|
|
18
|
+
DeleteTicket,
|
|
19
|
+
DeleteTicketTask,
|
|
20
|
+
DeleteTimelineDocument,
|
|
21
|
+
DeleteUser,
|
|
22
|
+
GetDocument,
|
|
23
|
+
GetEntity,
|
|
24
|
+
GetFollowup,
|
|
25
|
+
GetLocation,
|
|
26
|
+
GetSolution,
|
|
27
|
+
GetTeamMember,
|
|
28
|
+
GetTicket,
|
|
29
|
+
GetTicketTask,
|
|
30
|
+
GetTimelineDocument,
|
|
31
|
+
GetUser,
|
|
32
|
+
GlpiEnum,
|
|
33
|
+
GlpiGlobalValidation,
|
|
34
|
+
GlpiPriority,
|
|
35
|
+
GlpiSolutionStatus,
|
|
36
|
+
GlpiTaskState,
|
|
37
|
+
GlpiTicketContext,
|
|
38
|
+
GlpiTicketStatus,
|
|
39
|
+
GlpiTicketType,
|
|
40
|
+
GlpiTimelinePosition,
|
|
41
|
+
GlpiUserAuthType,
|
|
42
|
+
IdNameCompletenameRef,
|
|
43
|
+
IdNameRef,
|
|
44
|
+
IdRef,
|
|
45
|
+
PatchDocument,
|
|
46
|
+
PatchEntity,
|
|
47
|
+
PatchFollowup,
|
|
48
|
+
PatchLocation,
|
|
49
|
+
PatchSolution,
|
|
50
|
+
PatchTeamMember,
|
|
51
|
+
PatchTicket,
|
|
52
|
+
PatchTicketTask,
|
|
53
|
+
PatchTimelineDocument,
|
|
54
|
+
PatchUser,
|
|
55
|
+
PostDocument,
|
|
56
|
+
PostEntity,
|
|
57
|
+
PostFollowup,
|
|
58
|
+
PostLocation,
|
|
59
|
+
PostSolution,
|
|
60
|
+
PostTeamMember,
|
|
61
|
+
PostTicket,
|
|
62
|
+
PostTicketTask,
|
|
63
|
+
PostTimelineDocument,
|
|
64
|
+
PostUser,
|
|
19
65
|
)
|
|
20
66
|
|
|
21
|
-
__version__ = "0.1
|
|
67
|
+
__version__ = "0.2.1"
|
|
22
68
|
|
|
23
69
|
__all__ = [
|
|
24
|
-
"
|
|
25
|
-
"
|
|
70
|
+
"DeleteDocument",
|
|
71
|
+
"DeleteEntity",
|
|
72
|
+
"DeleteFollowup",
|
|
73
|
+
"DeleteLocation",
|
|
74
|
+
"DeleteSolution",
|
|
75
|
+
"DeleteTeamMember",
|
|
76
|
+
"DeleteTicket",
|
|
77
|
+
"DeleteTicketTask",
|
|
78
|
+
"DeleteTimelineDocument",
|
|
79
|
+
"DeleteUser",
|
|
80
|
+
"GetDocument",
|
|
81
|
+
"GetEntity",
|
|
82
|
+
"GetFollowup",
|
|
83
|
+
"GetLocation",
|
|
84
|
+
"GetSolution",
|
|
85
|
+
"GetTeamMember",
|
|
86
|
+
"GetTicket",
|
|
87
|
+
"GetTicketTask",
|
|
88
|
+
"GetTimelineDocument",
|
|
89
|
+
"GetUser",
|
|
26
90
|
"GlpiClient",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
91
|
+
"GlpiEnum",
|
|
92
|
+
"GlpiGlobalValidation",
|
|
93
|
+
"GlpiPriority",
|
|
94
|
+
"GlpiSolutionStatus",
|
|
95
|
+
"GlpiTaskState",
|
|
96
|
+
"GlpiTicketContext",
|
|
97
|
+
"GlpiTicketStatus",
|
|
98
|
+
"GlpiTicketType",
|
|
99
|
+
"GlpiTimelinePosition",
|
|
100
|
+
"GlpiUserAuthType",
|
|
101
|
+
"IdNameCompletenameRef",
|
|
102
|
+
"IdNameRef",
|
|
103
|
+
"IdRef",
|
|
104
|
+
"PatchDocument",
|
|
105
|
+
"PatchEntity",
|
|
106
|
+
"PatchFollowup",
|
|
107
|
+
"PatchLocation",
|
|
108
|
+
"PatchSolution",
|
|
109
|
+
"PatchTeamMember",
|
|
110
|
+
"PatchTicket",
|
|
111
|
+
"PatchTicketTask",
|
|
112
|
+
"PatchTimelineDocument",
|
|
113
|
+
"PatchUser",
|
|
114
|
+
"PostDocument",
|
|
115
|
+
"PostEntity",
|
|
116
|
+
"PostFollowup",
|
|
117
|
+
"PostLocation",
|
|
118
|
+
"PostSolution",
|
|
119
|
+
"PostTeamMember",
|
|
120
|
+
"PostTicket",
|
|
121
|
+
"PostTicketTask",
|
|
122
|
+
"PostTimelineDocument",
|
|
123
|
+
"PostUser",
|
|
35
124
|
"__version__",
|
|
36
125
|
]
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"""Public authentication exports for the GLPI client package.
|
|
2
2
|
|
|
3
|
-
The authentication package
|
|
4
|
-
|
|
3
|
+
The authentication package owns the OAuth2 token manager used by the
|
|
4
|
+
asynchronous ``GlpiClient`` and the legacy v1 session wrapper used solely by
|
|
5
|
+
the management document upload mixin.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
from __future__ import annotations
|
|
8
9
|
|
|
10
|
+
from glpi_python_client.auth._v1_session import GLPIV1Session
|
|
9
11
|
from glpi_python_client.auth.auth import GLPITokenManager
|
|
10
12
|
|
|
11
|
-
__all__ = ["GLPITokenManager"]
|
|
13
|
+
__all__ = ["GLPITokenManager", "GLPIV1Session"]
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""GLPI v1 REST session used exclusively for document uploads.
|
|
2
2
|
|
|
3
|
-
The high-level
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The high-level async ``GlpiClient`` only relies on the legacy v1 API for the
|
|
4
|
+
``POST /Document`` multipart upload endpoint. The session wrapper below owns
|
|
5
|
+
the authenticated v1 lifecycle (init, refresh, kill) and exposes a single
|
|
6
|
+
``upload_document`` operation that the management mixin calls through
|
|
7
|
+
``asyncio.to_thread`` at the blocking HTTP boundary.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
10
|
from __future__ import annotations
|
|
@@ -22,20 +24,11 @@ _AUTH_FAILURE_STATUS_CODES = frozenset({401, 403})
|
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
class GLPIV1Session:
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
The v1 API base URL.
|
|
31
|
-
user_token : str
|
|
32
|
-
The ``user_token`` credential for v1 authentication.
|
|
33
|
-
app_token : str
|
|
34
|
-
The ``App-Token`` value.
|
|
35
|
-
verify_ssl : bool, optional
|
|
36
|
-
Whether to verify SSL certificates.
|
|
37
|
-
session_refresh_interval_seconds : int, optional
|
|
38
|
-
Maximum age of a v1 session token before it is renewed.
|
|
27
|
+
"""Authenticated GLPI v1 REST session limited to document upload.
|
|
28
|
+
|
|
29
|
+
The session takes care of token initialisation, periodic refresh, retry on
|
|
30
|
+
auth failure, and best-effort cleanup. Only the upload endpoint is exposed
|
|
31
|
+
because the rest of the high-level client uses the v2 API exclusively.
|
|
39
32
|
"""
|
|
40
33
|
|
|
41
34
|
def __init__(
|
|
@@ -68,17 +61,10 @@ class GLPIV1Session:
|
|
|
68
61
|
|
|
69
62
|
@retry(stop=stop_after_attempt(3), wait=wait_fixed(3))
|
|
70
63
|
def _init_session(self) -> None:
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
Returns
|
|
74
|
-
-------
|
|
75
|
-
None
|
|
76
|
-
Stores the session token.
|
|
64
|
+
"""Acquire one fresh GLPI v1 session token via ``GET /initSession``.
|
|
77
65
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
ValueError
|
|
81
|
-
If the server does not return a valid session token.
|
|
66
|
+
The call replaces any existing session state and stores the
|
|
67
|
+
authentication timestamp used by the refresh-interval check.
|
|
82
68
|
"""
|
|
83
69
|
|
|
84
70
|
headers: dict[str, str] = {
|
|
@@ -109,12 +95,10 @@ class GLPIV1Session:
|
|
|
109
95
|
logger.info("GLPI v1 session initialised.")
|
|
110
96
|
|
|
111
97
|
def _ensure_session(self) -> None:
|
|
112
|
-
"""Lazily initialise or renew the v1 session
|
|
98
|
+
"""Lazily initialise or renew the v1 session when the token is stale.
|
|
113
99
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
None
|
|
117
|
-
Ensures the session token exists.
|
|
100
|
+
The helper is called by every authenticated request before any header
|
|
101
|
+
construction so callers never have to manage the lifecycle directly.
|
|
118
102
|
"""
|
|
119
103
|
|
|
120
104
|
if self._session_token is None:
|
|
@@ -125,13 +109,10 @@ class GLPIV1Session:
|
|
|
125
109
|
self._renew_session()
|
|
126
110
|
|
|
127
111
|
def _is_session_stale(self) -> bool:
|
|
128
|
-
"""Return whether the current v1 session
|
|
112
|
+
"""Return whether the current v1 session token must be renewed.
|
|
129
113
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
bool
|
|
133
|
-
``True`` when the session age is beyond the configured refresh
|
|
134
|
-
interval.
|
|
114
|
+
Stale tokens are determined by the configured refresh interval relative
|
|
115
|
+
to the timestamp the current token was acquired.
|
|
135
116
|
"""
|
|
136
117
|
|
|
137
118
|
if self._session_started_at is None:
|
|
@@ -141,12 +122,10 @@ class GLPIV1Session:
|
|
|
141
122
|
)
|
|
142
123
|
|
|
143
124
|
def _session_headers(self) -> dict[str, str]:
|
|
144
|
-
"""Return headers
|
|
125
|
+
"""Return the GLPI v1 headers carrying the current session token.
|
|
145
126
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
dict[str, str]
|
|
149
|
-
Headers including the current session token.
|
|
127
|
+
The helper assumes ``_ensure_session`` has already been called so the
|
|
128
|
+
session token state is valid.
|
|
150
129
|
"""
|
|
151
130
|
|
|
152
131
|
headers: dict[str, str] = {
|
|
@@ -158,12 +137,10 @@ class GLPIV1Session:
|
|
|
158
137
|
return headers
|
|
159
138
|
|
|
160
139
|
def _renew_session(self) -> None:
|
|
161
|
-
"""
|
|
140
|
+
"""Drop the current GLPI v1 session token and acquire a new one.
|
|
162
141
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
None
|
|
166
|
-
Mutates the stored session token.
|
|
142
|
+
The previous token is best-effort killed so the GLPI server can release
|
|
143
|
+
the associated session state immediately.
|
|
167
144
|
"""
|
|
168
145
|
|
|
169
146
|
old_token = self._session_token
|
|
@@ -181,12 +158,10 @@ class GLPIV1Session:
|
|
|
181
158
|
self._init_session()
|
|
182
159
|
|
|
183
160
|
def _headers(self) -> dict[str, str]:
|
|
184
|
-
"""Return
|
|
161
|
+
"""Return ready-to-use authenticated GLPI v1 request headers.
|
|
185
162
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
dict[str, str]
|
|
189
|
-
Headers including the session token.
|
|
163
|
+
The helper is the single header entry-point used by all authenticated
|
|
164
|
+
v1 calls so token lifecycle handling stays centralised.
|
|
190
165
|
"""
|
|
191
166
|
|
|
192
167
|
self._ensure_session()
|
|
@@ -200,23 +175,10 @@ class GLPIV1Session:
|
|
|
200
175
|
headers: dict[str, str] | None = None,
|
|
201
176
|
**kwargs: object,
|
|
202
177
|
) -> requests.Response:
|
|
203
|
-
"""
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
method : str
|
|
208
|
-
HTTP method.
|
|
209
|
-
url : str
|
|
210
|
-
Absolute request URL.
|
|
211
|
-
headers : dict[str, str] | None, optional
|
|
212
|
-
Additional request headers.
|
|
213
|
-
**kwargs : object
|
|
214
|
-
Additional ``requests`` keyword arguments.
|
|
215
|
-
|
|
216
|
-
Returns
|
|
217
|
-
-------
|
|
218
|
-
requests.Response
|
|
219
|
-
Raw response from GLPI.
|
|
178
|
+
"""Send one authenticated GLPI v1 request with one auth-failure retry.
|
|
179
|
+
|
|
180
|
+
When the GLPI server rejects the current token, the helper renews the
|
|
181
|
+
session and retries the request once before returning the response.
|
|
220
182
|
"""
|
|
221
183
|
|
|
222
184
|
request_headers = {**self._headers(), **(headers or {})}
|
|
@@ -239,63 +201,11 @@ class GLPIV1Session:
|
|
|
239
201
|
request_method(url, headers=request_headers, **kwargs),
|
|
240
202
|
)
|
|
241
203
|
|
|
242
|
-
@retry(stop=stop_after_attempt(3), wait=wait_fixed(3))
|
|
243
|
-
def get_sub_items(
|
|
244
|
-
self,
|
|
245
|
-
itemtype: str,
|
|
246
|
-
item_id: str | int,
|
|
247
|
-
sub_itemtype: str,
|
|
248
|
-
) -> list[dict[str, object]]:
|
|
249
|
-
"""Fetch legacy GLPI sub-items for one parent item.
|
|
250
|
-
|
|
251
|
-
Parameters
|
|
252
|
-
----------
|
|
253
|
-
itemtype : str
|
|
254
|
-
Parent GLPI itemtype.
|
|
255
|
-
item_id : str | int
|
|
256
|
-
Parent item identifier.
|
|
257
|
-
sub_itemtype : str
|
|
258
|
-
Child itemtype to fetch.
|
|
259
|
-
|
|
260
|
-
Returns
|
|
261
|
-
-------
|
|
262
|
-
list[dict[str, object]]
|
|
263
|
-
Raw sub-item payloads.
|
|
264
|
-
|
|
265
|
-
Raises
|
|
266
|
-
------
|
|
267
|
-
ValueError
|
|
268
|
-
If the request fails or returns an unexpected payload shape.
|
|
269
|
-
"""
|
|
270
|
-
|
|
271
|
-
response = self._authenticated_request(
|
|
272
|
-
"GET",
|
|
273
|
-
f"{self._base_url}/{itemtype}/{item_id}/{sub_itemtype}",
|
|
274
|
-
timeout=30,
|
|
275
|
-
)
|
|
276
|
-
if response.status_code not in (200, 206):
|
|
277
|
-
raise ValueError(
|
|
278
|
-
"GLPI v1 sub-item fetch failed: "
|
|
279
|
-
f"{response.status_code} {response.text[:300]}"
|
|
280
|
-
)
|
|
281
|
-
|
|
282
|
-
payload = response.json()
|
|
283
|
-
if isinstance(payload, list):
|
|
284
|
-
return [item for item in payload if isinstance(item, dict)]
|
|
285
|
-
if isinstance(payload, dict):
|
|
286
|
-
return [payload]
|
|
287
|
-
raise ValueError(
|
|
288
|
-
"GLPI v1 sub-item fetch returned unexpected payload: "
|
|
289
|
-
f"{type(payload).__name__}"
|
|
290
|
-
)
|
|
291
|
-
|
|
292
204
|
def close(self) -> None:
|
|
293
|
-
"""Kill the v1 session and release
|
|
205
|
+
"""Kill the v1 session token and release the underlying HTTP session.
|
|
294
206
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
None
|
|
298
|
-
Best-effort cleanup.
|
|
207
|
+
Cleanup is best-effort: any failure during ``killSession`` is logged
|
|
208
|
+
and the local session state is still cleared.
|
|
299
209
|
"""
|
|
300
210
|
|
|
301
211
|
try:
|
|
@@ -324,32 +234,11 @@ class GLPIV1Session:
|
|
|
324
234
|
ticket_id: int | None = None,
|
|
325
235
|
entity_id: int | None = None,
|
|
326
236
|
) -> dict[str, object]:
|
|
327
|
-
"""Upload
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
File name.
|
|
333
|
-
content : bytes
|
|
334
|
-
Raw file bytes.
|
|
335
|
-
mime_type : str
|
|
336
|
-
MIME type.
|
|
337
|
-
document_name : str | None, optional
|
|
338
|
-
GLPI display name.
|
|
339
|
-
ticket_id : int | None, optional
|
|
340
|
-
GLPI ticket ID used to link the document during creation.
|
|
341
|
-
entity_id : int | None, optional
|
|
342
|
-
GLPI entity ID assigned to the created document.
|
|
343
|
-
|
|
344
|
-
Returns
|
|
345
|
-
-------
|
|
346
|
-
dict[str, object]
|
|
347
|
-
GLPI response payload.
|
|
348
|
-
|
|
349
|
-
Raises
|
|
350
|
-
------
|
|
351
|
-
ValueError
|
|
352
|
-
If upload fails.
|
|
237
|
+
"""Upload one binary document via ``POST /Document``.
|
|
238
|
+
|
|
239
|
+
The legacy v1 endpoint uses a multipart upload manifest so the GLPI
|
|
240
|
+
server can create the document, link it to the optional parent ticket,
|
|
241
|
+
and assign it to the provided entity in a single round-trip.
|
|
353
242
|
"""
|
|
354
243
|
|
|
355
244
|
manifest_input: dict[str, object] = {
|
|
@@ -386,75 +275,17 @@ class GLPIV1Session:
|
|
|
386
275
|
logger.info("GLPI v1 document uploaded: id=%s", payload.get("id"))
|
|
387
276
|
return cast(dict[str, object], payload)
|
|
388
277
|
|
|
389
|
-
@retry(stop=stop_after_attempt(3), wait=wait_fixed(3))
|
|
390
|
-
def link_document_to_ticket(
|
|
391
|
-
self, document_id: int, ticket_id: int
|
|
392
|
-
) -> dict[str, object]:
|
|
393
|
-
"""Link an existing document to a ticket.
|
|
394
|
-
|
|
395
|
-
Parameters
|
|
396
|
-
----------
|
|
397
|
-
document_id : int
|
|
398
|
-
GLPI document ID.
|
|
399
|
-
ticket_id : int
|
|
400
|
-
GLPI ticket ID.
|
|
401
|
-
|
|
402
|
-
Returns
|
|
403
|
-
-------
|
|
404
|
-
dict[str, object]
|
|
405
|
-
GLPI response payload.
|
|
406
|
-
|
|
407
|
-
Raises
|
|
408
|
-
------
|
|
409
|
-
ValueError
|
|
410
|
-
If the link creation fails.
|
|
411
|
-
"""
|
|
412
|
-
|
|
413
|
-
payload = json.dumps(
|
|
414
|
-
{
|
|
415
|
-
"input": {
|
|
416
|
-
"documents_id": document_id,
|
|
417
|
-
"itemtype": "Ticket",
|
|
418
|
-
"items_id": ticket_id,
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
)
|
|
422
|
-
response = self._authenticated_request(
|
|
423
|
-
"POST",
|
|
424
|
-
f"{self._base_url}/Document_Item",
|
|
425
|
-
headers={"Content-Type": "application/json"},
|
|
426
|
-
data=payload,
|
|
427
|
-
timeout=30,
|
|
428
|
-
)
|
|
429
|
-
if response.status_code not in (200, 201):
|
|
430
|
-
raise ValueError(
|
|
431
|
-
"GLPI v1 Document_Item link failed: "
|
|
432
|
-
f"{response.status_code} {response.text[:300]}"
|
|
433
|
-
)
|
|
434
|
-
result = response.json()
|
|
435
|
-
if not isinstance(result, dict):
|
|
436
|
-
raise ValueError(
|
|
437
|
-
"GLPI v1 Document_Item link returned unexpected payload: "
|
|
438
|
-
f"{type(result).__name__}"
|
|
439
|
-
)
|
|
440
|
-
logger.info("GLPI v1 document %d linked to ticket %d", document_id, ticket_id)
|
|
441
|
-
return cast(dict[str, object], result)
|
|
442
|
-
|
|
443
278
|
|
|
444
279
|
def _is_auth_failure_response(response: requests.Response) -> bool:
|
|
445
|
-
"""Return whether one GLPI v1 response means the session is invalid.
|
|
446
|
-
|
|
447
|
-
Parameters
|
|
448
|
-
----------
|
|
449
|
-
response : requests.Response
|
|
450
|
-
Raw GLPI response.
|
|
280
|
+
"""Return whether one GLPI v1 response means the session token is invalid.
|
|
451
281
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
bool
|
|
455
|
-
``True`` when the response indicates an expired or rejected v1 session.
|
|
282
|
+
Both HTTP-level rejection and the ``ERROR_SESSION_TOKEN_INVALID`` payload
|
|
283
|
+
marker emitted by the GLPI v1 API are considered auth failures.
|
|
456
284
|
"""
|
|
457
285
|
|
|
458
286
|
if response.status_code in _AUTH_FAILURE_STATUS_CODES:
|
|
459
287
|
return True
|
|
460
288
|
return "ERROR_SESSION_TOKEN_INVALID" in str(response.text or "")
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
__all__ = ["GLPIV1Session"]
|