glpi-utils 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,423 @@
1
+ Metadata-Version: 2.4
2
+ Name: glpi-utils
3
+ Version: 1.2.0
4
+ Summary: Python library for working with the GLPI 11 REST API
5
+ Home-page: https://github.com/giovanny07/python-glpi-utils
6
+ Author: Giovanny Rodriguez
7
+ Author-email: giovanny@imagunet.com
8
+ License-Expression: MIT
9
+ Project-URL: Bug Tracker, https://github.com/giovanny07/python-glpi-utils/issues
10
+ Project-URL: Changelog, https://github.com/giovanny07/python-glpi-utils/blob/main/CHANGELOG.md
11
+ Project-URL: Documentation, https://github.com/giovanny07/python-glpi-utils#readme
12
+ Keywords: glpi,itsm,api,rest,helpdesk,monitoring,itil
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: System :: Systems Administration
25
+ Classifier: Topic :: Internet :: WWW/HTTP
26
+ Requires-Python: >=3.9
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: requests>=2.28.0
30
+ Provides-Extra: async
31
+ Requires-Dist: aiohttp>=3.9.0; extra == "async"
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0; extra == "dev"
34
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
35
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
36
+ Requires-Dist: mypy>=1.0; extra == "dev"
37
+ Requires-Dist: ruff>=0.3; extra == "dev"
38
+ Dynamic: license-file
39
+ Dynamic: license-expression
40
+
41
+ # python-glpi-utils
42
+
43
+ [![Tests](https://github.com/giovanny07/python-glpi-utils/actions/workflows/tests.yaml/badge.svg)](https://github.com/giovanny07/python-glpi-utils/actions/workflows/tests.yaml)
44
+ [![PyPI version](https://img.shields.io/pypi/v/glpi-utils.svg)](https://pypi.org/project/glpi-utils/)
45
+ [![Python versions](https://img.shields.io/pypi/pyversions/glpi-utils.svg)](https://pypi.org/project/glpi-utils/)
46
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
47
+
48
+ **python-glpi-utils** is a Python library for working with the [GLPI 11](https://glpi-project.org/) REST API.
49
+
50
+ It provides:
51
+
52
+ - A **synchronous** client (`GlpiAPI`) powered by `requests`.
53
+ - An **asynchronous** client (`AsyncGlpiAPI`) powered by `aiohttp`.
54
+ - **OAuth2 clients** (`GlpiOAuthClient` / `AsyncGlpiOAuthClient`) for the GLPI 11 high-level API (`/api.php`) with automatic token refresh.
55
+ - **Auto-pagination** (`get_all_pages` / `iter_pages`) — fetch every item across all pages with one call.
56
+ - Fluent **item-type accessors** (`api.ticket`, `api.computer`, `api.user`, …) so you write `api.ticket.get_all_pages()` instead of building raw HTTP calls.
57
+ - A `SensitiveFilter` that masks passwords and tokens in debug logs automatically.
58
+ - A **`GLPIVersion`** helper for comparing GLPI API versions.
59
+ - A clean **exception hierarchy** so you can catch exactly what you need.
60
+
61
+ > **Scope:** This library supports both the GLPI 11 legacy REST API (`/apirest.php`) and the high-level OAuth2 API (`/api.php`).
62
+
63
+ ---
64
+
65
+ ## Requirements
66
+
67
+ | Dependency | Version |
68
+ |------------|---------|
69
+ | Python | ≥ 3.9 |
70
+ | GLPI | 11.x |
71
+ | requests | ≥ 2.28 |
72
+ | aiohttp | ≥ 3.9 *(async only)* |
73
+
74
+ ---
75
+
76
+ ## Installation
77
+
78
+ ### From PyPI
79
+
80
+ ```bash
81
+ pip install glpi-utils
82
+ ```
83
+
84
+ With async support:
85
+
86
+ ```bash
87
+ pip install glpi-utils[async]
88
+ ```
89
+
90
+ ### From source
91
+
92
+ ```bash
93
+ git clone https://github.com/giovanny07/python-glpi-utils
94
+ cd python-glpi-utils
95
+ pip install -e .[async]
96
+ ```
97
+
98
+ ---
99
+
100
+ ## Quick start
101
+
102
+ ### Synchronous
103
+
104
+ ```python
105
+ from glpi_utils import GlpiAPI
106
+
107
+ api = GlpiAPI(url="https://glpi.example.com", app_token="YOUR_APP_TOKEN")
108
+ api.login(username="glpi", password="glpi")
109
+
110
+ print("GLPI version:", api.version) # GLPIVersion('11.0.0')
111
+ print(api.version > 10.0) # True
112
+
113
+ tickets = api.ticket.get_all(range="0-9", expand_dropdowns=True)
114
+ for t in tickets:
115
+ print(f"[{t['id']}] {t['name']}")
116
+
117
+ api.logout()
118
+ ```
119
+
120
+ ### Asynchronous
121
+
122
+ ```python
123
+ import asyncio
124
+ from glpi_utils import AsyncGlpiAPI
125
+
126
+ async def main():
127
+ api = AsyncGlpiAPI(url="https://glpi.example.com")
128
+ await api.login(username="glpi", password="glpi")
129
+
130
+ version = await api.get_version()
131
+ print("GLPI version:", version)
132
+
133
+ tickets = await api.ticket.get_all(range="0-9")
134
+ for t in tickets:
135
+ print(f"[{t['id']}] {t['name']}")
136
+
137
+ await api.logout()
138
+
139
+ asyncio.run(main())
140
+ ```
141
+
142
+ ### Context manager (auto-logout)
143
+
144
+ ```python
145
+ # Sync
146
+ with GlpiAPI(url="https://glpi.example.com") as api:
147
+ api.login(username="glpi", password="glpi")
148
+ print(api.version)
149
+
150
+ # Async
151
+ async with AsyncGlpiAPI(url="https://glpi.example.com") as api:
152
+ await api.login(username="glpi", password="glpi")
153
+ version = await api.get_version()
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Authentication
159
+
160
+ Three methods are supported (sync and async):
161
+
162
+ ```python
163
+ # 1. Username + password (Basic Auth)
164
+ api.login(username="glpi", password="glpi")
165
+
166
+ # 2. Personal API token (from user profile → Remote access key)
167
+ api.login(user_token="q56hqkniwot8wntb3z1qarka5atf365taaa2uyjrn")
168
+
169
+ # 3. Environment variables (no arguments needed)
170
+ # GLPI_URL, GLPI_USER, GLPI_PASSWORD, GLPI_USER_TOKEN, GLPI_APP_TOKEN
171
+ api = GlpiAPI() # reads GLPI_URL
172
+ api.login() # reads GLPI_USER + GLPI_PASSWORD or GLPI_USER_TOKEN
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Item-type accessors
178
+
179
+ All standard GLPI item types are available as attributes:
180
+
181
+ | Attribute | GLPI itemtype |
182
+ |---------------------|--------------------|
183
+ | `api.ticket` | `Ticket` |
184
+ | `api.computer` | `Computer` |
185
+ | `api.monitor` | `Monitor` |
186
+ | `api.printer` | `Printer` |
187
+ | `api.networkequipment` | `NetworkEquipment` |
188
+ | `api.software` | `Software` |
189
+ | `api.user` | `User` |
190
+ | `api.group` | `Group` |
191
+ | `api.entity` | `Entity` |
192
+ | `api.location` | `Location` |
193
+ | `api.category` | `ITILCategory` |
194
+ | `api.problem` | `Problem` |
195
+ | `api.change` | `Change` |
196
+ | `api.project` | `Project` |
197
+ | `api.projecttask` | `ProjectTask` |
198
+ | `api.document` | `Document` |
199
+ | `api.contract` | `Contract` |
200
+ | `api.knowledgebase` | `KnowbaseItem` |
201
+ | `api.followup` | `ITILFollowup` |
202
+ | `api.solution` | `ITILSolution` |
203
+ | `api.task` | `TicketTask` |
204
+
205
+ For any other item type use `api.item("YourItemtype")`:
206
+
207
+ ```python
208
+ proxy = api.item("KnowbaseItem")
209
+ articles = proxy.get_all(range="0-4")
210
+ ```
211
+
212
+ ---
213
+
214
+ ## Auto-pagination
215
+
216
+ By default `get_all()` returns a single page (50 items). Use `get_all_pages()` to retrieve everything automatically:
217
+
218
+ ```python
219
+ # All tickets — GLPI handles pagination transparently
220
+ all_tickets = api.ticket.get_all_pages()
221
+
222
+ # With filters
223
+ open_tickets = api.ticket.get_all_pages(
224
+ sort="date_mod",
225
+ order="DESC",
226
+ is_deleted=False,
227
+ )
228
+
229
+ # Custom page size (fewer round-trips on fast networks)
230
+ computers = api.computer.get_all_pages(page_size=100, expand_dropdowns=True)
231
+
232
+ print(f"Total: {len(all_tickets)} tickets")
233
+ ```
234
+
235
+ For large datasets, use `iter_pages()` to process items batch by batch without loading everything into RAM:
236
+
237
+ ```python
238
+ total = 0
239
+ for page in api.ticket.iter_pages(page_size=100):
240
+ for ticket in page:
241
+ process(ticket)
242
+ total += 1
243
+ print(f"Processed {total} tickets")
244
+
245
+ # Async version
246
+ async for page in api.ticket.iter_pages(page_size=100):
247
+ for ticket in page:
248
+ await process(ticket)
249
+ ```
250
+
251
+ ---
252
+
253
+ ## OAuth2 (High-level API)
254
+
255
+ For the GLPI 11 high-level API (`/api.php`), use the OAuth2 clients:
256
+
257
+ ```python
258
+ from glpi_utils.oauth import GlpiOAuthClient
259
+
260
+ # Client credentials grant (service accounts, scripts)
261
+ with GlpiOAuthClient(
262
+ url="https://glpi.example.com",
263
+ client_id="my-app",
264
+ client_secret="my-secret",
265
+ ) as api:
266
+ api.authenticate()
267
+ tickets = api.ticket.get_all_pages()
268
+
269
+ # Password grant (user-delegated)
270
+ api = GlpiOAuthClient(url="https://glpi.example.com", client_id="my-app")
271
+ api.authenticate(username="glpi", password="glpi")
272
+ computers = api.computer.get_all_pages()
273
+ api.close()
274
+
275
+ # Async
276
+ from glpi_utils.oauth import AsyncGlpiOAuthClient
277
+
278
+ async with AsyncGlpiOAuthClient(
279
+ url="https://glpi.example.com",
280
+ client_id="my-app",
281
+ client_secret="my-secret",
282
+ ) as api:
283
+ await api.authenticate()
284
+ tickets = await api.ticket.get_all_pages()
285
+ ```
286
+
287
+ The OAuth2 clients support all the same CRUD, sub-item, search and pagination methods as `GlpiAPI`.
288
+
289
+ Environment variables: `GLPI_OAUTH_CLIENT_ID`, `GLPI_OAUTH_CLIENT_SECRET`, `GLPI_OAUTH_USERNAME`, `GLPI_OAUTH_PASSWORD`.
290
+
291
+ ---
292
+
293
+ ## CRUD operations
294
+
295
+ Every item-type accessor exposes the same set of methods:
296
+
297
+ ```python
298
+ # Read
299
+ ticket = api.ticket.get(1, expand_dropdowns=True)
300
+ tickets = api.ticket.get_all(range="0-49", sort="date_mod", order="DESC")
301
+
302
+ # Search engine
303
+ results = api.ticket.search(
304
+ criteria=[{"field": 12, "searchtype": "equals", "value": 1}],
305
+ forcedisplay=[1, 3, 12],
306
+ range="0-49",
307
+ )
308
+
309
+ # Create
310
+ new = api.ticket.create({
311
+ "name": "Service degraded",
312
+ "content": "Users report slow response times.",
313
+ "type": 1,
314
+ "status": 1,
315
+ "urgency": 3,
316
+ "impact": 3,
317
+ "priority": 3,
318
+ })
319
+
320
+ # Update (id required)
321
+ api.ticket.update({"id": new["id"], "status": 2}) # Assigned
322
+
323
+ # Delete
324
+ api.ticket.delete({"id": new["id"]}) # to trash
325
+ api.ticket.delete({"id": new["id"]}, force_purge=True) # permanent
326
+ ```
327
+
328
+ ### Sub-items (followups, tasks, solutions)
329
+
330
+ ```python
331
+ # Read followups
332
+ followups = api.ticket.get_sub_items(1, "ITILFollowup")
333
+
334
+ # Add a followup
335
+ api.ticket.add_sub_item(1, "ITILFollowup", {
336
+ "content": "Confirmed issue on node 3.",
337
+ "is_private": 0,
338
+ })
339
+ ```
340
+
341
+ ---
342
+
343
+ ## GLPIVersion
344
+
345
+ ```python
346
+ ver = api.version
347
+ print(type(ver).__name__, ver) # GLPIVersion 11.0.0
348
+
349
+ print(ver > 10.0) # True
350
+ print(ver == "11.0.0") # True
351
+ print(ver.major) # 11
352
+ print(ver.minor) # 0
353
+ print(ver.patch) # 0
354
+ ```
355
+
356
+ ---
357
+
358
+ ## Error handling
359
+
360
+ ```python
361
+ from glpi_utils import (
362
+ GlpiError,
363
+ GlpiAPIError,
364
+ GlpiAuthError,
365
+ GlpiNotFoundError,
366
+ GlpiPermissionError,
367
+ )
368
+
369
+ try:
370
+ ticket = api.ticket.get(99999)
371
+ except GlpiNotFoundError:
372
+ print("Ticket does not exist")
373
+ except GlpiPermissionError:
374
+ print("Insufficient rights")
375
+ except GlpiAuthError:
376
+ print("Session expired – re-login")
377
+ except GlpiAPIError as e:
378
+ print(f"API error {e.error_code}: {e.message}")
379
+ except GlpiError:
380
+ print("Generic library error")
381
+ ```
382
+
383
+ ---
384
+
385
+ ## Enabling debug logging
386
+
387
+ The library is silent by default. Enable with standard `logging`:
388
+
389
+ ```python
390
+ import logging
391
+ logging.basicConfig(level=logging.DEBUG)
392
+
393
+ from glpi_utils import GlpiAPI
394
+ api = GlpiAPI(url="https://glpi.example.com")
395
+ api.login(username="glpi", password="glpi")
396
+ ```
397
+
398
+ Passwords, tokens and session IDs are **masked automatically** by `SensitiveFilter`. To add your own handler with masking:
399
+
400
+ ```python
401
+ from glpi_utils import SensitiveFilter
402
+
403
+ handler = logging.StreamHandler()
404
+ handler.addFilter(SensitiveFilter())
405
+ logging.getLogger("glpi_utils").addHandler(handler)
406
+ logging.getLogger("glpi_utils").setLevel(logging.DEBUG)
407
+ ```
408
+
409
+ ---
410
+
411
+ ## Running the tests
412
+
413
+ ```bash
414
+ pip install -e .[async]
415
+ pip install -r requirements-dev.txt
416
+ pytest
417
+ ```
418
+
419
+ ---
420
+
421
+ ## License
422
+
423
+ **python-glpi-utils** is distributed under the [MIT License](LICENSE).