glpi-python-client 0.2.1__tar.gz → 0.3.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.
Files changed (146) hide show
  1. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/CONTRIBUTING.md +5 -1
  2. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/PKG-INFO +58 -36
  3. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/README.md +57 -35
  4. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/docs/api_reference.rst +20 -2
  5. glpi_python_client-0.3.0/docs/development.md +109 -0
  6. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/docs/index.rst +1 -0
  7. glpi_python_client-0.3.0/docs/sponsoring.rst +9 -0
  8. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/docs/user_guide.rst +247 -241
  9. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/__init__.py +15 -5
  10. glpi_python_client-0.3.0/glpi_python_client/clients/__init__.py +20 -0
  11. glpi_python_client-0.3.0/glpi_python_client/clients/api/__init__.py +40 -0
  12. glpi_python_client-0.3.0/glpi_python_client/clients/api/administration/__init__.py +13 -0
  13. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/administration/_entity.py +15 -17
  14. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/administration/_user.py +16 -16
  15. glpi_python_client-0.3.0/glpi_python_client/clients/api/assistance/__init__.py +8 -0
  16. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/assistance/_team.py +11 -13
  17. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/assistance/_ticket.py +15 -17
  18. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/assistance/timeline/__init__.py +9 -9
  19. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/assistance/timeline/_document.py +16 -16
  20. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/assistance/timeline/_followup.py +16 -18
  21. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/assistance/timeline/_solution.py +16 -18
  22. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/assistance/timeline/_task.py +16 -18
  23. glpi_python_client-0.3.0/glpi_python_client/clients/api/dropdowns/__init__.py +7 -0
  24. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/dropdowns/_location.py +15 -17
  25. glpi_python_client-0.3.0/glpi_python_client/clients/api/management/__init__.py +7 -0
  26. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/api/management/_document.py +25 -28
  27. glpi_python_client-0.3.0/glpi_python_client/clients/async_client.py +241 -0
  28. glpi_python_client-0.3.0/glpi_python_client/clients/commons/_async_bridge.py +118 -0
  29. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/_config.py +2 -1
  30. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/_transport.py +89 -61
  31. glpi_python_client-0.3.0/glpi_python_client/clients/commons/tests/test_transport.py +136 -0
  32. glpi_python_client-0.3.0/glpi_python_client/clients/custom/__init__.py +32 -0
  33. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/custom/_statistics.py +11 -14
  34. glpi_python_client-0.3.0/glpi_python_client/clients/custom/_statistics_async.py +69 -0
  35. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/custom/_ticket_context.py +17 -16
  36. glpi_python_client-0.3.0/glpi_python_client/clients/custom/_ticket_context_async.py +73 -0
  37. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/custom/tests/test_statistics.py +14 -16
  38. glpi_python_client-0.3.0/glpi_python_client/clients/custom/tests/test_ticket_context.py +57 -0
  39. glpi_python_client-0.2.1/glpi_python_client/clients/glpi_client.py → glpi_python_client-0.3.0/glpi_python_client/clients/sync_client.py +55 -54
  40. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/tests/test_api_coverage.py +87 -87
  41. glpi_python_client-0.3.0/glpi_python_client/clients/tests/test_async_branches.py +137 -0
  42. glpi_python_client-0.3.0/glpi_python_client/clients/tests/test_async_smoke.py +150 -0
  43. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/tests/test_glpi_client.py +14 -14
  44. glpi_python_client-0.3.0/glpi_python_client/clients/tests/test_parity.py +64 -0
  45. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/tests/test_smoke.py +28 -28
  46. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/__init__.py +5 -1
  47. glpi_python_client-0.3.0/glpi_python_client/models/api_schema/administration/_entity.py +95 -0
  48. glpi_python_client-0.3.0/glpi_python_client/models/api_schema/administration/_user.py +357 -0
  49. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/administration/tests/test_administration_schemas.py +31 -0
  50. glpi_python_client-0.3.0/glpi_python_client/models/api_schema/assistance/_team.py +84 -0
  51. glpi_python_client-0.3.0/glpi_python_client/models/api_schema/assistance/_ticket.py +345 -0
  52. glpi_python_client-0.3.0/glpi_python_client/models/api_schema/assistance/timeline/_document.py +102 -0
  53. glpi_python_client-0.3.0/glpi_python_client/models/api_schema/assistance/timeline/_followup.py +166 -0
  54. glpi_python_client-0.3.0/glpi_python_client/models/api_schema/assistance/timeline/_solution.py +156 -0
  55. glpi_python_client-0.3.0/glpi_python_client/models/api_schema/assistance/timeline/_task.py +210 -0
  56. glpi_python_client-0.3.0/glpi_python_client/models/api_schema/dropdowns/_location.py +193 -0
  57. glpi_python_client-0.3.0/glpi_python_client/models/api_schema/management/_document.py +130 -0
  58. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/custom_schema/__init__.py +2 -1
  59. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/custom_schema/_ticket_context.py +143 -39
  60. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/custom_schema/tests/test_ticket_context.py +189 -1
  61. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/testing/fixtures.py +1 -1
  62. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/testing/utils.py +15 -2
  63. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/pyproject.toml +1 -1
  64. glpi_python_client-0.3.0/skills/glpi-client-setup/SKILL.md +157 -0
  65. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/skills/glpi-document-workflow/SKILL.md +3 -2
  66. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/skills/glpi-reporting-and-context/SKILL.md +3 -2
  67. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/skills/glpi-team-members/SKILL.md +3 -2
  68. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/skills/glpi-ticket-timeline/SKILL.md +3 -2
  69. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/skills/glpi-ticket-workflow/SKILL.md +3 -2
  70. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/skills/glpi-user-location-provisioning/SKILL.md +3 -2
  71. glpi_python_client-0.2.1/docs/development.md +0 -96
  72. glpi_python_client-0.2.1/docs/usage.md +0 -381
  73. glpi_python_client-0.2.1/glpi_python_client/clients/__init__.py +0 -13
  74. glpi_python_client-0.2.1/glpi_python_client/clients/api/__init__.py +0 -40
  75. glpi_python_client-0.2.1/glpi_python_client/clients/api/administration/__init__.py +0 -12
  76. glpi_python_client-0.2.1/glpi_python_client/clients/api/assistance/__init__.py +0 -8
  77. glpi_python_client-0.2.1/glpi_python_client/clients/api/dropdowns/__init__.py +0 -7
  78. glpi_python_client-0.2.1/glpi_python_client/clients/api/management/__init__.py +0 -7
  79. glpi_python_client-0.2.1/glpi_python_client/clients/custom/__init__.py +0 -15
  80. glpi_python_client-0.2.1/glpi_python_client/models/api_schema/administration/_entity.py +0 -55
  81. glpi_python_client-0.2.1/glpi_python_client/models/api_schema/administration/_user.py +0 -132
  82. glpi_python_client-0.2.1/glpi_python_client/models/api_schema/assistance/_team.py +0 -59
  83. glpi_python_client-0.2.1/glpi_python_client/models/api_schema/assistance/_ticket.py +0 -156
  84. glpi_python_client-0.2.1/glpi_python_client/models/api_schema/assistance/timeline/_document.py +0 -60
  85. glpi_python_client-0.2.1/glpi_python_client/models/api_schema/assistance/timeline/_followup.py +0 -78
  86. glpi_python_client-0.2.1/glpi_python_client/models/api_schema/assistance/timeline/_solution.py +0 -76
  87. glpi_python_client-0.2.1/glpi_python_client/models/api_schema/assistance/timeline/_task.py +0 -97
  88. glpi_python_client-0.2.1/glpi_python_client/models/api_schema/dropdowns/_location.py +0 -86
  89. glpi_python_client-0.2.1/glpi_python_client/models/api_schema/management/_document.py +0 -67
  90. glpi_python_client-0.2.1/skills/glpi-client-setup/SKILL.md +0 -90
  91. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/.gitignore +0 -0
  92. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/.pre-commit-config.yaml +0 -0
  93. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/.readthedocs.yaml +0 -0
  94. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/LICENSE +0 -0
  95. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/docs/_static/.gitkeep +0 -0
  96. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/docs/conf.py +0 -0
  97. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/docs/development_rtd.rst +0 -0
  98. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/docs/glpi_api_contract.json +0 -0
  99. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/docs/installation.rst +0 -0
  100. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/docs/publishing.md +0 -0
  101. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/docs/publishing_rtd.rst +0 -0
  102. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/auth/__init__.py +0 -0
  103. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/auth/_v1_session.py +0 -0
  104. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/auth/auth.py +0 -0
  105. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/auth/tests/test_auth.py +0 -0
  106. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/auth/tests/test_v1_session.py +0 -0
  107. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/__init__.py +0 -0
  108. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/_constants.py +0 -0
  109. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/_errors.py +0 -0
  110. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/_filters.py +0 -0
  111. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/_http.py +0 -0
  112. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/_payloads.py +0 -0
  113. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/tests/__init__.py +0 -0
  114. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/tests/test_errors.py +0 -0
  115. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/tests/test_filters.py +0 -0
  116. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/tests/test_http.py +0 -0
  117. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/commons/tests/test_payloads.py +0 -0
  118. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/clients/tests/__init__.py +0 -0
  119. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/content/__init__.py +0 -0
  120. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/content/conversion.py +0 -0
  121. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/content/tests/__init__.py +0 -0
  122. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/content/tests/test_conversion.py +0 -0
  123. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/_base.py +0 -0
  124. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/__init__.py +0 -0
  125. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/_common.py +0 -0
  126. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/_content.py +0 -0
  127. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/administration/__init__.py +0 -0
  128. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/administration/tests/__init__.py +0 -0
  129. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/assistance/__init__.py +0 -0
  130. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/assistance/tests/__init__.py +0 -0
  131. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/assistance/tests/test_assistance_schemas.py +0 -0
  132. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/assistance/tests/test_content_roundtrip.py +0 -0
  133. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/assistance/timeline/__init__.py +0 -0
  134. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/assistance/timeline/tests/__init__.py +0 -0
  135. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/assistance/timeline/tests/test_timeline_schemas.py +0 -0
  136. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/dropdowns/__init__.py +0 -0
  137. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/dropdowns/tests/__init__.py +0 -0
  138. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/dropdowns/tests/test_dropdowns_schemas.py +0 -0
  139. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/enums.py +0 -0
  140. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/management/__init__.py +0 -0
  141. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/management/tests/__init__.py +0 -0
  142. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/api_schema/management/tests/test_management_schemas.py +0 -0
  143. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/models/custom_schema/tests/__init__.py +0 -0
  144. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/py.typed +0 -0
  145. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/glpi_python_client/testing/__init__.py +0 -0
  146. {glpi_python_client-0.2.1 → glpi_python_client-0.3.0}/skills/README.md +0 -0
@@ -50,7 +50,11 @@ python -m sphinx -W --keep-going -b html docs docs/_build/html
50
50
 
51
51
  ## Design Guidelines
52
52
 
53
- - Keep API calls behind `GlpiClient` methods.
53
+ - Keep API calls behind `GlpiClient` / `AsyncGlpiClient` methods. Add
54
+ new endpoints to a sync endpoint mixin only; `AsyncGlpiClient`
55
+ exposes them as coroutines automatically through `AsyncBridge`. Only
56
+ add a dedicated async override when the method needs concurrent
57
+ fan-out via `asyncio.gather`.
54
58
  - Prefer field-validated Pydantic models for request and response payloads.
55
59
  - Avoid organization-specific category, entity, or profile defaults in the
56
60
  library core.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glpi-python-client
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: A typed Python client for GLPI ITSM APIs.
5
5
  Project-URL: Homepage, https://github.com/baraline/glpi_python_client
6
6
  Project-URL: Documentation, https://glpi-python-client.readthedocs.io/en/latest/
@@ -62,7 +62,7 @@ Description-Content-Type: text/markdown
62
62
  [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://github.com/baraline/glpi_python_client)
63
63
  [![Docs](https://readthedocs.org/projects/glpi-python-client/badge/?version=latest)](https://glpi-python-client.readthedocs.io/en/latest/)
64
64
 
65
- `glpi-python-client` is a typed Python client for GLPI ITSM APIs.
65
+ `glpi-python-client` is a typed Python client for the GLPI REST API.
66
66
 
67
67
  The goal is to let GLPI integrations work with domain objects instead of raw
68
68
  JSON payloads. The package exposes Pydantic models for tickets, users,
@@ -70,8 +70,16 @@ followups, documents, locations, and related records, while converting GLPI
70
70
  HTML content into Markdown for Python-side workflows and rendering Markdown
71
71
  back to HTML for outgoing payloads.
72
72
 
73
- It currently focuses on ticket-centric workflows and exposes a single
74
- asynchronous high-level client built on top of the GLPI v2 REST API.
73
+ It currently focuses on ticket-centric workflows and exposes two high-level
74
+ clients built on top of the GLPI v2 REST API:
75
+
76
+ - `GlpiClient` — synchronous, blocking client (single source of truth for
77
+ endpoint behaviour).
78
+ - `AsyncGlpiClient` — asynchronous facade that wraps every synchronous
79
+ method into a coroutine and dispatches it to a worker thread.
80
+
81
+ Note that all integration tests using this package are made on GLPI 11.
82
+ I cannot make any guarantee of the behaviour on previous versions.
75
83
 
76
84
  While the package is preparing for 1.0, alot of potential breaking change might happen between versions. A deprecation policy will be put in place once 1.0 is out and the package have been stabilized.
77
85
 
@@ -96,14 +104,38 @@ Create a client with your GLPI v2 API URL and at least one complete auth pair:
96
104
  - `username` and `password`
97
105
  - both pairs together
98
106
 
107
+ ### Synchronous client
108
+
109
+ ```python
110
+ from glpi_python_client import GlpiClient, PostTicket
111
+
112
+ with GlpiClient(
113
+ glpi_api_url="https://glpi.example.com/api.php/v2",
114
+ client_id="oauth-client-id",
115
+ client_secret="oauth-client-secret",
116
+ username="api-user",
117
+ password="api-password",
118
+ ) as glpi:
119
+ ticket_id = glpi.create_ticket(
120
+ PostTicket(
121
+ name="Printer issue",
122
+ content="The printer is not reachable from the office network.",
123
+ )
124
+ )
125
+ ticket = glpi.get_ticket(ticket_id)
126
+ print(ticket.id, ticket.name)
127
+ ```
128
+
129
+ ### Asynchronous client
130
+
99
131
  ```python
100
132
  import asyncio
101
133
 
102
- from glpi_python_client import GlpiClient, PostTicket
134
+ from glpi_python_client import AsyncGlpiClient, PostTicket
103
135
 
104
136
 
105
137
  async def main() -> None:
106
- async with GlpiClient(
138
+ async with AsyncGlpiClient(
107
139
  glpi_api_url="https://glpi.example.com/api.php/v2",
108
140
  client_id="oauth-client-id",
109
141
  client_secret="oauth-client-secret",
@@ -123,38 +155,22 @@ async def main() -> None:
123
155
  asyncio.run(main())
124
156
  ```
125
157
 
126
- If your application already provides `GLPI_` environment variables,
127
- `GlpiClient.from_env()` is also available.
158
+ `GlpiClient.from_env()` and `AsyncGlpiClient.from_env()` are also available
159
+ when the credentials are already exposed as `GLPI_`-prefixed environment
160
+ variables.
128
161
 
129
- ### Calling from synchronous code
162
+ ### Sync or async?
130
163
 
131
- The client is async-only, but it works from sync programs through
132
- `asyncio.run`. Wrap the calls in a coroutine and execute it once:
133
-
134
- ```python
135
- import asyncio
136
-
137
- from glpi_python_client import GlpiClient
138
-
139
-
140
- def fetch_open_tickets() -> list[int]:
141
- async def _run() -> list[int]:
142
- async with GlpiClient.from_env() as glpi:
143
- tickets = await glpi.search_tickets("status==1", limit=10)
144
- return [ticket.id for ticket in tickets]
145
-
146
- return asyncio.run(_run())
147
-
148
-
149
- if __name__ == "__main__":
150
- print(fetch_open_tickets())
151
- ```
152
-
153
- For long-lived sync services that need many calls, run a dedicated
154
- event loop on a background thread and dispatch with
155
- `asyncio.run_coroutine_threadsafe`. See the
156
- [user guide](https://glpi-python-client.readthedocs.io/en/latest/user_guide.html#calling-the-client-from-synchronous-code)
157
- for the full pattern.
164
+ Both clients expose the exact same endpoint surface and accept the same
165
+ constructor arguments. The async client is a thin facade that wraps each
166
+ synchronous method into a coroutine dispatched to a worker thread via
167
+ `asyncio.to_thread` (or a caller-supplied `concurrent.futures.Executor`).
168
+ A shared `threading.Lock` serialises OAuth token acquisition so concurrent
169
+ `asyncio.gather(...)` fan-outs cannot race. Pick `GlpiClient` for plain
170
+ scripts, CLI tools, and synchronous services; pick `AsyncGlpiClient` when
171
+ your application already runs an event loop or when you need concurrent
172
+ fan-out (the aggregated `get_ticket_context` and per-ticket
173
+ `get_task_statistics` helpers use `asyncio.gather` on the async client).
158
174
 
159
175
  ## Documentation
160
176
 
@@ -169,3 +185,9 @@ To build the Sphinx documentation locally:
169
185
  python -m pip install -e .[docs]
170
186
  python -m sphinx -b html docs docs/_build/html
171
187
  ```
188
+
189
+ ## Sponsoring & Professional services
190
+ The development of this package is indirectly supported by [Novahé](https://www.novahe.fr/) & [Constellation](https://www.constellation.fr/).
191
+
192
+ If you need professional help or services around GLPI, we offer consulting and engineering services to install, maintain or upgarde GLPI instance, as an [official GLPI partner](https://www.glpi-project.org/fr/new-glpi-silver-partner-in-france-novahe/).
193
+
@@ -6,7 +6,7 @@
6
6
  [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://github.com/baraline/glpi_python_client)
7
7
  [![Docs](https://readthedocs.org/projects/glpi-python-client/badge/?version=latest)](https://glpi-python-client.readthedocs.io/en/latest/)
8
8
 
9
- `glpi-python-client` is a typed Python client for GLPI ITSM APIs.
9
+ `glpi-python-client` is a typed Python client for the GLPI REST API.
10
10
 
11
11
  The goal is to let GLPI integrations work with domain objects instead of raw
12
12
  JSON payloads. The package exposes Pydantic models for tickets, users,
@@ -14,8 +14,16 @@ followups, documents, locations, and related records, while converting GLPI
14
14
  HTML content into Markdown for Python-side workflows and rendering Markdown
15
15
  back to HTML for outgoing payloads.
16
16
 
17
- It currently focuses on ticket-centric workflows and exposes a single
18
- asynchronous high-level client built on top of the GLPI v2 REST API.
17
+ It currently focuses on ticket-centric workflows and exposes two high-level
18
+ clients built on top of the GLPI v2 REST API:
19
+
20
+ - `GlpiClient` — synchronous, blocking client (single source of truth for
21
+ endpoint behaviour).
22
+ - `AsyncGlpiClient` — asynchronous facade that wraps every synchronous
23
+ method into a coroutine and dispatches it to a worker thread.
24
+
25
+ Note that all integration tests using this package are made on GLPI 11.
26
+ I cannot make any guarantee of the behaviour on previous versions.
19
27
 
20
28
  While the package is preparing for 1.0, alot of potential breaking change might happen between versions. A deprecation policy will be put in place once 1.0 is out and the package have been stabilized.
21
29
 
@@ -40,14 +48,38 @@ Create a client with your GLPI v2 API URL and at least one complete auth pair:
40
48
  - `username` and `password`
41
49
  - both pairs together
42
50
 
51
+ ### Synchronous client
52
+
53
+ ```python
54
+ from glpi_python_client import GlpiClient, PostTicket
55
+
56
+ with GlpiClient(
57
+ glpi_api_url="https://glpi.example.com/api.php/v2",
58
+ client_id="oauth-client-id",
59
+ client_secret="oauth-client-secret",
60
+ username="api-user",
61
+ password="api-password",
62
+ ) as glpi:
63
+ ticket_id = glpi.create_ticket(
64
+ PostTicket(
65
+ name="Printer issue",
66
+ content="The printer is not reachable from the office network.",
67
+ )
68
+ )
69
+ ticket = glpi.get_ticket(ticket_id)
70
+ print(ticket.id, ticket.name)
71
+ ```
72
+
73
+ ### Asynchronous client
74
+
43
75
  ```python
44
76
  import asyncio
45
77
 
46
- from glpi_python_client import GlpiClient, PostTicket
78
+ from glpi_python_client import AsyncGlpiClient, PostTicket
47
79
 
48
80
 
49
81
  async def main() -> None:
50
- async with GlpiClient(
82
+ async with AsyncGlpiClient(
51
83
  glpi_api_url="https://glpi.example.com/api.php/v2",
52
84
  client_id="oauth-client-id",
53
85
  client_secret="oauth-client-secret",
@@ -67,38 +99,22 @@ async def main() -> None:
67
99
  asyncio.run(main())
68
100
  ```
69
101
 
70
- If your application already provides `GLPI_` environment variables,
71
- `GlpiClient.from_env()` is also available.
102
+ `GlpiClient.from_env()` and `AsyncGlpiClient.from_env()` are also available
103
+ when the credentials are already exposed as `GLPI_`-prefixed environment
104
+ variables.
72
105
 
73
- ### Calling from synchronous code
106
+ ### Sync or async?
74
107
 
75
- The client is async-only, but it works from sync programs through
76
- `asyncio.run`. Wrap the calls in a coroutine and execute it once:
77
-
78
- ```python
79
- import asyncio
80
-
81
- from glpi_python_client import GlpiClient
82
-
83
-
84
- def fetch_open_tickets() -> list[int]:
85
- async def _run() -> list[int]:
86
- async with GlpiClient.from_env() as glpi:
87
- tickets = await glpi.search_tickets("status==1", limit=10)
88
- return [ticket.id for ticket in tickets]
89
-
90
- return asyncio.run(_run())
91
-
92
-
93
- if __name__ == "__main__":
94
- print(fetch_open_tickets())
95
- ```
96
-
97
- For long-lived sync services that need many calls, run a dedicated
98
- event loop on a background thread and dispatch with
99
- `asyncio.run_coroutine_threadsafe`. See the
100
- [user guide](https://glpi-python-client.readthedocs.io/en/latest/user_guide.html#calling-the-client-from-synchronous-code)
101
- for the full pattern.
108
+ Both clients expose the exact same endpoint surface and accept the same
109
+ constructor arguments. The async client is a thin facade that wraps each
110
+ synchronous method into a coroutine dispatched to a worker thread via
111
+ `asyncio.to_thread` (or a caller-supplied `concurrent.futures.Executor`).
112
+ A shared `threading.Lock` serialises OAuth token acquisition so concurrent
113
+ `asyncio.gather(...)` fan-outs cannot race. Pick `GlpiClient` for plain
114
+ scripts, CLI tools, and synchronous services; pick `AsyncGlpiClient` when
115
+ your application already runs an event loop or when you need concurrent
116
+ fan-out (the aggregated `get_ticket_context` and per-ticket
117
+ `get_task_statistics` helpers use `asyncio.gather` on the async client).
102
118
 
103
119
  ## Documentation
104
120
 
@@ -113,3 +129,9 @@ To build the Sphinx documentation locally:
113
129
  python -m pip install -e .[docs]
114
130
  python -m sphinx -b html docs docs/_build/html
115
131
  ```
132
+
133
+ ## Sponsoring & Professional services
134
+ The development of this package is indirectly supported by [Novahé](https://www.novahe.fr/) & [Constellation](https://www.constellation.fr/).
135
+
136
+ If you need professional help or services around GLPI, we offer consulting and engineering services to install, maintain or upgarde GLPI instance, as an [official GLPI partner](https://www.glpi-project.org/fr/new-glpi-silver-partner-in-france-novahe/).
137
+
@@ -7,14 +7,27 @@ underscore-prefixed helpers are intentionally omitted.
7
7
 
8
8
  .. currentmodule:: glpi_python_client
9
9
 
10
- Client
11
- ------
10
+ Clients
11
+ -------
12
+
13
+ The package exposes two clients with identical endpoint surfaces. The
14
+ synchronous one is the single source of truth for endpoint behaviour;
15
+ the asynchronous one wraps each synchronous method into a coroutine.
12
16
 
13
17
  .. autoclass:: GlpiClient
14
18
  :members:
15
19
  :inherited-members:
16
20
  :show-inheritance:
17
21
 
22
+ .. autoclass:: AsyncGlpiClient
23
+ :members:
24
+ :inherited-members:
25
+ :show-inheritance:
26
+
27
+ .. autoclass:: glpi_python_client.clients.commons._async_bridge.AsyncBridge
28
+ :members:
29
+ :show-inheritance:
30
+
18
31
  Aggregated Models
19
32
  -----------------
20
33
 
@@ -23,6 +36,11 @@ Aggregated Models
23
36
  :undoc-members:
24
37
  :show-inheritance:
25
38
 
39
+ .. autoclass:: TicketMarkdownOptions
40
+ :members:
41
+ :undoc-members:
42
+ :show-inheritance:
43
+
26
44
  Common Reference Models
27
45
  -----------------------
28
46
 
@@ -0,0 +1,109 @@
1
+ # Development Guide
2
+
3
+ ## Local Setup
4
+
5
+ Create a virtual environment and install the package with development dependencies:
6
+
7
+ ```bash
8
+ python -m venv .venv
9
+ .venv\Scripts\activate
10
+ python -m pip install --upgrade pip
11
+ python -m pip install -e .[dev]
12
+ python -m pre_commit install
13
+ ```
14
+
15
+ The repository ships a root `.pre-commit-config.yaml` that runs Ruff on each
16
+ commit. The lint hook applies safe fixes first, then Ruff formats the touched
17
+ files.
18
+
19
+ ## Checks
20
+
21
+ Run these before publishing or opening a pull request:
22
+
23
+ ```bash
24
+ python -m pre_commit run --all-files
25
+ python -m pytest
26
+ python -m ruff check .
27
+ python -m mypy glpi_python_client
28
+ python -m sphinx -b html docs docs/_build/html
29
+ python -m build
30
+ python -m vulture glpi_python_client --min-confidence 80
31
+ ```
32
+
33
+ If your global Python environment has broken pytest plugins, run the suite with
34
+ plugin autoload disabled:
35
+
36
+ ```bash
37
+ $env:PYTEST_DISABLE_PLUGIN_AUTOLOAD = "1"
38
+ python -m pytest
39
+ ```
40
+
41
+ ## Package Layout
42
+
43
+ - `glpi_python_client.__init__` exposes the public import surface,
44
+ including both client classes and the Pydantic models.
45
+ - `glpi_python_client.clients.sync_client.GlpiClient` is the
46
+ synchronous, blocking client. It is the single source of truth for
47
+ endpoint behaviour: each public method lives on one of the sync
48
+ endpoint mixins under `glpi_python_client.clients.api.*` and
49
+ `glpi_python_client.clients.custom.*`.
50
+ - `glpi_python_client.clients.async_client.AsyncGlpiClient` is the
51
+ asynchronous facade. It inherits the same endpoint mixins and uses
52
+ `glpi_python_client.clients.commons._async_bridge.AsyncBridge` to wrap
53
+ every inherited public sync method into a coroutine dispatched on a
54
+ worker thread (`asyncio.to_thread` by default, or a caller-supplied
55
+ `concurrent.futures.Executor`).
56
+ - `glpi_python_client.clients.commons` holds the reusable building
57
+ blocks shared by every endpoint mixin: configuration helpers
58
+ (`_config`), constants (`_constants`), errors (`_errors`), filters
59
+ (`_filters`), HTTP helpers (`_http`), payload builders (`_payloads`),
60
+ the synchronous `TransportMixin` (`_transport`), and the
61
+ `AsyncBridge` (`_async_bridge`). A shared `threading.Lock` in the
62
+ transport serialises OAuth token acquisition so concurrent
63
+ `asyncio.gather` fan-outs on the async client cannot race.
64
+ - `glpi_python_client.clients.api.*` contains the contract-aligned
65
+ synchronous endpoint mixins, grouped by GLPI subtree (administration,
66
+ assistance, assistance/timeline, dropdowns, management).
67
+ - `glpi_python_client.clients.custom` contains custom helpers built on
68
+ top of the API mixins. Each helper has a synchronous implementation
69
+ (`_ticket_context.py`, `_statistics.py`) plus an optional async
70
+ override (`_ticket_context_async.py`, `_statistics_async.py`) that
71
+ fans the underlying calls out concurrently with `asyncio.gather`.
72
+ - `glpi_python_client.auth._v1_session` contains the legacy v1
73
+ session used for binary document uploads.
74
+ - `glpi_python_client.models` contains typed request and response
75
+ models.
76
+ - `glpi_python_client.content` handles HTML/Markdown conversion for
77
+ ticket descriptions, followups, tasks, and solutions.
78
+ - `glpi_python_client.testing` exposes `make_client` and
79
+ `make_async_client` factories that produce in-memory clients with no
80
+ real HTTP plumbing for downstream test suites.
81
+ - `docs` contains the Read the Docs/Sphinx documentation source.
82
+ - `skills` contains contributor-facing Agent Skills for repository
83
+ workflows. The source distribution includes them for source consumers
84
+ and contributors, but the wheel still installs only the
85
+ `glpi_python_client` runtime package.
86
+
87
+ ## Adding Endpoints
88
+
89
+ 1. Add or extend a model in `glpi_python_client.models`.
90
+ 2. Add the client method on the matching **synchronous** endpoint mixin
91
+ under `glpi_python_client.clients.api.*` (or
92
+ `glpi_python_client.clients.custom.*` for derived helpers). The
93
+ async client picks the new method up automatically through the
94
+ `AsyncBridge` — do not duplicate the method on a parallel async
95
+ mixin unless you genuinely need concurrent fan-out (`asyncio.gather`)
96
+ inside the method body.
97
+ 3. Put reusable endpoint names, payload builders, response handling, or
98
+ pagination logic in the focused
99
+ `glpi_python_client.clients.commons` helper module named for that
100
+ responsibility.
101
+ 4. Add unit tests for payload serialization, response parsing, and
102
+ client behavior. The parity test in
103
+ `glpi_python_client/clients/tests/test_parity.py` will fail if the
104
+ sync and async surfaces diverge.
105
+ 5. Document the new workflow in `docs/user_guide.rst` or the README.
106
+
107
+ Keep organization-specific defaults outside the package core.
108
+ Applications can map their own entities, profiles, and categories
109
+ before calling the client.
@@ -24,6 +24,7 @@ handling, and helpers for ticket, user, location, and document workflows.
24
24
 
25
25
  development_rtd
26
26
  publishing_rtd
27
+ sponsoring
27
28
 
28
29
  Indices and Tables
29
30
  ==================
@@ -0,0 +1,9 @@
1
+ Sponsoring & Professional Services
2
+ ===================================
3
+
4
+ The development of this package is indirectly supported by
5
+ `Novahé <https://www.novahe.fr/>`_ & `Constellation <https://www.constellation.fr/>`_.
6
+
7
+ If you need professional help or services around GLPI, we offer consulting and
8
+ engineering services to install, maintain, or upgrade GLPI instances, as an
9
+ `official GLPI partner <https://www.glpi-project.org/fr/new-glpi-silver-partner-in-france-novahe/>`_.