glpi-python-client 0.5.0__tar.gz → 0.6.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 (171) hide show
  1. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/.gitignore +1 -1
  2. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/CHANGELOG.md +89 -0
  3. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/CONTRIBUTING.md +17 -1
  4. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/PKG-INFO +9 -10
  5. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/README.md +1 -1
  6. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/api_reference.rst +131 -10
  7. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/conf.py +1 -5
  8. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/installation.rst +1 -1
  9. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/publishing_rtd.rst +1 -1
  10. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/user_guide.rst +239 -3
  11. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/__init__.py +43 -1
  12. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/auth/_v1_session.py +3 -3
  13. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/auth/auth.py +3 -3
  14. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/_base_client.py +1 -7
  15. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/__init__.py +13 -3
  16. glpi_python_client-0.6.0/glpi_python_client/_async/clients/api/assets/__init__.py +7 -0
  17. glpi_python_client-0.6.0/glpi_python_client/_async/clients/api/assets/_computer.py +427 -0
  18. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/dropdowns/__init__.py +4 -1
  19. glpi_python_client-0.6.0/glpi_python_client/_async/clients/api/dropdowns/_contract_type.py +220 -0
  20. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/management/__init__.py +2 -1
  21. glpi_python_client-0.6.0/glpi_python_client/_async/clients/api/management/_contract.py +411 -0
  22. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/client.py +7 -6
  23. {glpi_python_client-0.5.0/glpi_python_client/_sync → glpi_python_client-0.6.0/glpi_python_client/_async}/clients/commons/_constants.py +9 -1
  24. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/auth/_v1_session.py +3 -3
  25. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/auth/auth.py +3 -3
  26. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/_base_client.py +1 -7
  27. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/__init__.py +13 -3
  28. glpi_python_client-0.6.0/glpi_python_client/_sync/clients/api/assets/__init__.py +7 -0
  29. glpi_python_client-0.6.0/glpi_python_client/_sync/clients/api/assets/_computer.py +427 -0
  30. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/dropdowns/__init__.py +4 -1
  31. glpi_python_client-0.6.0/glpi_python_client/_sync/clients/api/dropdowns/_contract_type.py +220 -0
  32. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/management/__init__.py +2 -1
  33. glpi_python_client-0.6.0/glpi_python_client/_sync/clients/api/management/_contract.py +411 -0
  34. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/client.py +7 -6
  35. {glpi_python_client-0.5.0/glpi_python_client/_async → glpi_python_client-0.6.0/glpi_python_client/_sync}/clients/commons/_constants.py +9 -1
  36. glpi_python_client-0.6.0/glpi_python_client/content/conversion.py +675 -0
  37. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/__init__.py +45 -1
  38. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/__init__.py +1 -1
  39. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/_content.py +9 -10
  40. glpi_python_client-0.6.0/glpi_python_client/models/api_schema/assets/__init__.py +25 -0
  41. glpi_python_client-0.6.0/glpi_python_client/models/api_schema/assets/_computer.py +257 -0
  42. glpi_python_client-0.6.0/glpi_python_client/models/api_schema/assets/_contract_item.py +102 -0
  43. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/dropdowns/__init__.py +10 -0
  44. glpi_python_client-0.6.0/glpi_python_client/models/api_schema/dropdowns/_contract_type.py +100 -0
  45. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/enums.py +14 -0
  46. glpi_python_client-0.6.0/glpi_python_client/models/api_schema/management/__init__.py +35 -0
  47. glpi_python_client-0.6.0/glpi_python_client/models/api_schema/management/_contract.py +328 -0
  48. glpi_python_client-0.6.0/glpi_python_client/models/api_schema/management/_contract_cost.py +139 -0
  49. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/custom_schema/_ticket_context.py +18 -6
  50. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/pyproject.toml +15 -12
  51. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/skills/README.md +3 -1
  52. glpi_python_client-0.6.0/skills/glpi-asset-workflow/SKILL.md +89 -0
  53. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/skills/glpi-client-setup/SKILL.md +2 -2
  54. glpi_python_client-0.6.0/skills/glpi-contract-workflow/SKILL.md +123 -0
  55. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/skills/glpi-document-workflow/SKILL.md +2 -2
  56. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/skills/glpi-knowledge-base/SKILL.md +2 -2
  57. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/skills/glpi-plugin-fields/SKILL.md +2 -2
  58. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/skills/glpi-reporting-and-context/SKILL.md +2 -2
  59. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/skills/glpi-team-members/SKILL.md +2 -2
  60. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/skills/glpi-ticket-timeline/SKILL.md +4 -3
  61. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/skills/glpi-ticket-workflow/SKILL.md +4 -3
  62. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/skills/glpi-user-location-provisioning/SKILL.md +2 -2
  63. glpi_python_client-0.5.0/glpi_python_client/content/conversion.py +0 -729
  64. glpi_python_client-0.5.0/glpi_python_client/models/api_schema/management/__init__.py +0 -15
  65. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/.pre-commit-config.yaml +0 -0
  66. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/.readthedocs.yaml +0 -0
  67. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/LICENSE +0 -0
  68. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/_static/.gitkeep +0 -0
  69. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/development.md +0 -0
  70. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/development_rtd.rst +0 -0
  71. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/index.rst +0 -0
  72. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/publishing.md +0 -0
  73. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/docs/sponsoring.rst +0 -0
  74. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/__init__.py +0 -0
  75. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/_concurrency.py +0 -0
  76. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/_testing.py +0 -0
  77. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/auth/__init__.py +0 -0
  78. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/__init__.py +0 -0
  79. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/administration/__init__.py +0 -0
  80. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/administration/_entity.py +0 -0
  81. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/administration/_user.py +0 -0
  82. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/assistance/__init__.py +0 -0
  83. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/assistance/_team.py +0 -0
  84. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/assistance/_ticket.py +0 -0
  85. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/assistance/timeline/__init__.py +0 -0
  86. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/assistance/timeline/_document.py +0 -0
  87. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/assistance/timeline/_followup.py +0 -0
  88. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/assistance/timeline/_solution.py +0 -0
  89. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/assistance/timeline/_task.py +0 -0
  90. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/dropdowns/_location.py +0 -0
  91. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/knowledgebase/__init__.py +0 -0
  92. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/knowledgebase/_article.py +0 -0
  93. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/knowledgebase/_category.py +0 -0
  94. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/knowledgebase/_comment.py +0 -0
  95. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/knowledgebase/_revision.py +0 -0
  96. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/management/_document.py +0 -0
  97. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/plugins/__init__.py +0 -0
  98. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/api/plugins/_fields.py +0 -0
  99. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/commons/__init__.py +0 -0
  100. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/commons/_config.py +0 -0
  101. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/commons/_filters.py +0 -0
  102. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/commons/_http.py +0 -0
  103. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/commons/_payloads.py +0 -0
  104. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/commons/_transport.py +0 -0
  105. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/custom/__init__.py +0 -0
  106. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/custom/_statistics.py +0 -0
  107. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_async/clients/custom/_ticket_context.py +0 -0
  108. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_errors.py +0 -0
  109. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/__init__.py +0 -0
  110. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/_concurrency.py +0 -0
  111. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/_testing.py +0 -0
  112. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/auth/__init__.py +0 -0
  113. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/__init__.py +0 -0
  114. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/administration/__init__.py +0 -0
  115. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/administration/_entity.py +0 -0
  116. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/administration/_user.py +0 -0
  117. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/assistance/__init__.py +0 -0
  118. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/assistance/_team.py +0 -0
  119. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/assistance/_ticket.py +0 -0
  120. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/assistance/timeline/__init__.py +0 -0
  121. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/assistance/timeline/_document.py +0 -0
  122. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/assistance/timeline/_followup.py +0 -0
  123. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/assistance/timeline/_solution.py +0 -0
  124. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/assistance/timeline/_task.py +0 -0
  125. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/dropdowns/_location.py +0 -0
  126. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/knowledgebase/__init__.py +0 -0
  127. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/knowledgebase/_article.py +0 -0
  128. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/knowledgebase/_category.py +0 -0
  129. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/knowledgebase/_comment.py +0 -0
  130. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/knowledgebase/_revision.py +0 -0
  131. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/management/_document.py +0 -0
  132. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/plugins/__init__.py +0 -0
  133. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/api/plugins/_fields.py +0 -0
  134. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/commons/__init__.py +0 -0
  135. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/commons/_config.py +0 -0
  136. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/commons/_filters.py +0 -0
  137. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/commons/_http.py +0 -0
  138. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/commons/_payloads.py +0 -0
  139. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/commons/_transport.py +0 -0
  140. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/custom/__init__.py +0 -0
  141. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/custom/_statistics.py +0 -0
  142. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/_sync/clients/custom/_ticket_context.py +0 -0
  143. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/content/__init__.py +0 -0
  144. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/_base.py +0 -0
  145. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/_common.py +0 -0
  146. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/administration/__init__.py +0 -0
  147. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/administration/_entity.py +0 -0
  148. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/administration/_user.py +0 -0
  149. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/assistance/__init__.py +0 -0
  150. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/assistance/_team.py +0 -0
  151. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/assistance/_ticket.py +0 -0
  152. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/assistance/timeline/__init__.py +0 -0
  153. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/assistance/timeline/_document.py +0 -0
  154. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/assistance/timeline/_followup.py +0 -0
  155. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/assistance/timeline/_solution.py +0 -0
  156. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/assistance/timeline/_task.py +0 -0
  157. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/dropdowns/_location.py +0 -0
  158. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/knowledgebase/__init__.py +0 -0
  159. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/knowledgebase/_article.py +0 -0
  160. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/knowledgebase/_category.py +0 -0
  161. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/knowledgebase/_comment.py +0 -0
  162. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/knowledgebase/_revision.py +0 -0
  163. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/management/_document.py +0 -0
  164. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/plugins/__init__.py +0 -0
  165. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/api_schema/plugins/_fields.py +0 -0
  166. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/models/custom_schema/__init__.py +0 -0
  167. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/py.typed +0 -0
  168. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/rsql.py +0 -0
  169. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/testing/__init__.py +0 -0
  170. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/testing/fixtures.py +0 -0
  171. {glpi_python_client-0.5.0 → glpi_python_client-0.6.0}/glpi_python_client/testing/utils.py +0 -0
@@ -33,7 +33,7 @@ CLAUDE.md
33
33
  docs/superpowers/*
34
34
 
35
35
  # Large generated GLPI API contract (kept locally, not tracked)
36
- docs/glpi_api_contract.json
36
+ docs/api_contract/
37
37
  # Coverage data: rewritten by every test run (and by the venv .pth hook).
38
38
  .coverage
39
39
  .coverage.*
@@ -4,6 +4,95 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## 0.6.0 — 2026-10-01
8
+
9
+ ### Changed (breaking)
10
+
11
+ - **Python 3.10 is no longer supported; 3.11 is the minimum.** The
12
+ `typing-extensions` and `tomli` backports it needed are dropped.
13
+ - **Content conversion is rebuilt on three libraries: markdownify,
14
+ mdformat and cmark-gfm.** `from_transport` reads GLPI's HTML with
15
+ `markdownify`, and `mdformat` re-renders that Markdown from its syntax
16
+ tree, so it keeps only the escapes CommonMark needs. `to_transport`
17
+ renders through `cmark-gfm`, the GitHub reference implementation, in
18
+ place of python-markdown. A thin layer of glue sits on top. Measured on
19
+ 346 real bodies sampled from a GLPI 11 instance:
20
+ - 322 display the same after a round trip, against 299 before;
21
+ - 344 read back as the same Markdown, against 240.
22
+
23
+ Of 205 realistic caller-written Markdown documents, all 205 survive
24
+ Markdown → HTML → Markdown with the same display.
25
+ - **Markdown is rendered as CommonMark with GFM tables.** A newline is a
26
+ line break, as `nl2br` made it before. Raw HTML passes through.
27
+ Differences you may see in Markdown you write:
28
+ - a list or a table written straight after a line now starts a list or a
29
+ table, where python-markdown wanted a blank line first;
30
+ - lists nested by two or three spaces nest;
31
+ - `1)` starts a numbered list;
32
+ - `#Important`, with no space, is text rather than a heading;
33
+ - `*a **b** c*` keeps its bold;
34
+ - a backslash ending a line is a line break, so write `C:\Temp\` at the
35
+ end of a line as `` `C:\Temp\` ``;
36
+ - `<word>` is read as an HTML tag, so put a placeholder such as `<login>`
37
+ in backticks.
38
+ - **`.content` is spelled as canonical CommonMark.** A line break reads
39
+ back as `\` and a newline, a nested list is indented by its bullet's
40
+ width, and a table comes back unpadded. Text that would otherwise read as
41
+ syntax is escaped: `__init__` reads `\_\_init\_\_`, and a `* point` line
42
+ reads `\* point`. Stored digests of `.content` change once.
43
+ - **A plain-text body is literal text on the read path.** A value with no
44
+ HTML element used to come back verbatim and be rendered as Markdown. It is
45
+ now read as GLPI displays it, its lines as lines.
46
+ `GlpiContentConverter.from_transport` takes `plain_text_is_markdown`.
47
+ `True` is what the write models' validator passes: caller-authored
48
+ Markdown passes verbatim unless it starts with an HTML tag, so Markdown
49
+ carrying an inline `<br>` or `<kbd>` stays Markdown.
50
+ - **Dependencies.**
51
+ - Added: `cmarkgfm>=2025.10` (compiled wheels for CPython 3.11–3.14 on
52
+ Linux, macOS and Windows), `mdformat>=0.7.22,<0.8`,
53
+ `mdformat-tables>=1.0` and `markdown-it-py>=3.0`.
54
+ - Dropped: `markdown`. python-markdown 3.11 had broken the previous
55
+ reader.
56
+ - Raised: `beautifulsoup4>=4.15`, which fixed the parser defect that
57
+ dropped the text after a `<br />` in a body that also held a bare
58
+ `<br>`. That removes the workaround.
59
+
60
+ ### Fixed
61
+
62
+ - **Literal text came back as Markdown syntax.** These now read back as the
63
+ text a user typed:
64
+ - `\serveur\compta`, which had lost a backslash;
65
+ - `__init__` and `______`, which had become bold;
66
+ - a `-----` line under text, which had made a heading;
67
+ - `* point` and `> merci` lines, which had become a list and a quote;
68
+ - `[1]: https://...`, which had been consumed as a reference definition;
69
+ - a `|` in a table cell, which had dropped the rest of the row.
70
+ - **A table nested in a table cell lost all its text.** That is the usual
71
+ layout of an e-mail signature. The inner table is now written as its
72
+ cells' text, its line breaks kept as `<br>`.
73
+ - **Nested lists flattened on the first write.** Nested items, the text
74
+ after a nested list, and numbering, `start` included, survive.
75
+ - `<script>`, `<style>` and `<title>` bodies no longer leak into the text.
76
+ - A blank line inside a paragraph (`<br><br>`, or Outlook's
77
+ `<br>&nbsp;<br>`) is kept.
78
+ - A label in bold right before a figure, as in `<b>Total:</b>12`, keeps its
79
+ bold.
80
+ - A code fence keeps its language, and a `|` inside code in a table cell no
81
+ longer splits the row.
82
+ - A long body converts in linear time, and a body nested deeper than the
83
+ stack is read as its text instead of raising.
84
+
85
+ ### Known limitations
86
+
87
+ - A Markdown table needs a header row and the same number of cells in every
88
+ row. A header-less HTML table gains an empty header row, and a row that
89
+ spans the table gains empty cells.
90
+ - Struck-through text is kept as raw `<s>`, since CommonMark has no
91
+ strikethrough.
92
+ - Literal text that looks like syntax is sometimes escaped where
93
+ CommonMark would not need it, for example `5\*3` or `x \<= y`. It
94
+ displays as typed.
95
+
7
96
  ## 0.5.0 — 2026-09-08
8
97
 
9
98
  ### Fixed
@@ -38,7 +38,7 @@ python -m sphinx -W --keep-going -b html docs docs/_build/html
38
38
 
39
39
  ## GitHub Actions
40
40
 
41
- - `.github/workflows/ci.yml` runs tests for Python 3.10 through 3.14 on pull
41
+ - `.github/workflows/ci.yml` runs tests for Python 3.11 through 3.14 on pull
42
42
  requests and pushes to `main`.
43
43
  - The same workflow runs `ruff`, `mypy`, and a warning-free Sphinx build on
44
44
  Python 3.12.
@@ -63,3 +63,19 @@ python -m sphinx -W --keep-going -b html docs docs/_build/html
63
63
  library core.
64
64
  - Add tests for payload serialization and response normalization when adding
65
65
  endpoints.
66
+
67
+ ### The GLPI OpenAPI contract
68
+
69
+ The models under `glpi_python_client/models/api_schema/` mirror
70
+ `components.schemas.*` from the GLPI High-Level REST API document,
71
+ including which fields are `readOnly`. That document is not committed —
72
+ it is ~14 MB and instance-specific. Fetch your instance's copy to the
73
+ path the models cite:
74
+
75
+ ```bash
76
+ curl -sk "<your GLPI base>/api.php/v2.3/doc.json" -o docs/api_contract/api.json
77
+ ```
78
+
79
+ `docs/api_contract/` is gitignored. Nothing in CI checks the models
80
+ against it, so when you add or change a model, diff it against the
81
+ contract by hand.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: glpi-python-client
3
- Version: 0.5.0
3
+ Version: 0.6.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/
@@ -15,7 +15,6 @@ Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: MIT License
16
16
  Classifier: Operating System :: OS Independent
17
17
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.10
19
18
  Classifier: Programming Language :: Python :: 3.11
20
19
  Classifier: Programming Language :: Python :: 3.12
21
20
  Classifier: Programming Language :: Python :: 3.13
@@ -23,16 +22,18 @@ Classifier: Programming Language :: Python :: 3.14
23
22
  Classifier: Topic :: Internet :: WWW/HTTP
24
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
24
  Classifier: Typing :: Typed
26
- Requires-Python: >=3.10
27
- Requires-Dist: beautifulsoup4>=4.12
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: beautifulsoup4>=4.15
27
+ Requires-Dist: cmarkgfm>=2025.10
28
28
  Requires-Dist: httpx>=0.28
29
29
  Requires-Dist: lxml>=4.9
30
- Requires-Dist: markdown>=3.6
31
- Requires-Dist: markdownify>=0.13
30
+ Requires-Dist: markdown-it-py>=3.0
31
+ Requires-Dist: markdownify>=1.2
32
+ Requires-Dist: mdformat-tables>=1.0
33
+ Requires-Dist: mdformat<0.8,>=0.7.22
32
34
  Requires-Dist: pydantic>=2.8
33
35
  Requires-Dist: sniffio>=1.3
34
36
  Requires-Dist: tenacity>=8.2
35
- Requires-Dist: typing-extensions>=4.7; python_version < '3.11'
36
37
  Requires-Dist: tzdata>=2024.1; platform_system == 'Windows'
37
38
  Provides-Extra: dev
38
39
  Requires-Dist: build>=1.2; extra == 'dev'
@@ -45,7 +46,6 @@ Requires-Dist: pytest>=8.0; extra == 'dev'
45
46
  Requires-Dist: ruff>=0.6; extra == 'dev'
46
47
  Requires-Dist: sphinx-rtd-theme>=2.0; extra == 'dev'
47
48
  Requires-Dist: sphinx<8.2,>=7.2; extra == 'dev'
48
- Requires-Dist: tomli>=2.0; (python_version < '3.11') and extra == 'dev'
49
49
  Requires-Dist: twine>=5.1; extra == 'dev'
50
50
  Requires-Dist: unasync>=0.6; extra == 'dev'
51
51
  Requires-Dist: vulture>=2.11; extra == 'dev'
@@ -53,7 +53,6 @@ Provides-Extra: docs
53
53
  Requires-Dist: numpydoc>=1.8; extra == 'docs'
54
54
  Requires-Dist: sphinx-rtd-theme>=2.0; extra == 'docs'
55
55
  Requires-Dist: sphinx<8.2,>=7.2; extra == 'docs'
56
- Requires-Dist: tomli>=2.0; (python_version < '3.11') and extra == 'docs'
57
56
  Description-Content-Type: text/markdown
58
57
 
59
58
  # glpi-python-client
@@ -61,7 +60,7 @@ Description-Content-Type: text/markdown
61
60
  [![CI](https://github.com/baraline/glpi_python_client/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/baraline/glpi_python_client/actions/workflows/ci.yml)
62
61
  [![Coverage](https://codecov.io/gh/baraline/glpi_python_client/branch/main/graph/badge.svg)](https://codecov.io/gh/baraline/glpi_python_client)
63
62
  [![License](https://img.shields.io/github/license/baraline/glpi_python_client)](LICENSE)
64
- [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://github.com/baraline/glpi_python_client)
63
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://github.com/baraline/glpi_python_client)
65
64
  [![Docs](https://readthedocs.org/projects/glpi-python-client/badge/?version=latest)](https://glpi-python-client.readthedocs.io/en/latest/)
66
65
 
67
66
  `glpi-python-client` is a typed Python client for the GLPI REST API.
@@ -3,7 +3,7 @@
3
3
  [![CI](https://github.com/baraline/glpi_python_client/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/baraline/glpi_python_client/actions/workflows/ci.yml)
4
4
  [![Coverage](https://codecov.io/gh/baraline/glpi_python_client/branch/main/graph/badge.svg)](https://codecov.io/gh/baraline/glpi_python_client)
5
5
  [![License](https://img.shields.io/github/license/baraline/glpi_python_client)](LICENSE)
6
- [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://github.com/baraline/glpi_python_client)
6
+ [![Python](https://img.shields.io/badge/python-3.11%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
9
  `glpi-python-client` is a typed Python client for the GLPI REST API.
@@ -113,18 +113,19 @@ conversion happens, which buys two things: listing records costs nothing
113
113
  per body, and a body that cannot be converted no longer stops the rest of
114
114
  its page being read.
115
115
 
116
+ The Markdown is CommonMark with GFM tables, rendered by cmark-gfm. It
117
+ spells a body's text as literal text -- ``\_\_init\_\_``, ``\\\serveur``,
118
+ ``\# pas un titre`` -- so rendering it displays what GLPI displayed and
119
+ reading that back gives the same Markdown. A write model keeps the caller's
120
+ Markdown verbatim unless it starts with an HTML tag. See
121
+ :ref:`content-conversion`.
122
+
116
123
  Very deeply nested HTML is the case worth knowing about.
117
124
  ``markdownify`` walks the document recursively and runs out of stack at
118
- around 494 levels of nesting. The converter does not try to predict
119
- that: it attempts the conversion and, if the walk does not fit, strips
120
- tags instead. It degrades, it never truncates, and it does not raise:
121
- every character the normal rendering would have produced still appears.
122
- What is lost is structure rather than words — link targets and image alt
123
- text, code fencing and ``<pre>`` indentation, ``&nbsp;`` alignment.
124
- Because the budget is the stack left when the conversion starts, the
125
- same body can convert from one call site and degrade from a deeper one.
126
- Anything else that goes wrong in either direction raises
127
- :class:`GlpiContentError`.
125
+ a few hundred levels of nesting. The converter attempts the conversion
126
+ and, if the walk does not fit, reads the body's text instead, a line per
127
+ block: the words survive, the structure does not. Anything else that goes
128
+ wrong in either direction raises :class:`GlpiContentError`.
128
129
 
129
130
  Because the conversion is cached on first read, a read model should be
130
131
  treated as immutable afterwards: assigning to ``content_html``, or
@@ -499,6 +500,121 @@ The companion mixin methods are exposed on :class:`GlpiClient` /
499
500
  :undoc-members:
500
501
  :show-inheritance:
501
502
 
503
+ Computers
504
+ ---------
505
+
506
+ .. autoclass:: GetComputer
507
+ :members:
508
+ :undoc-members:
509
+ :show-inheritance:
510
+
511
+ .. autoclass:: PostComputer
512
+ :members:
513
+ :undoc-members:
514
+ :show-inheritance:
515
+
516
+ .. autoclass:: PatchComputer
517
+ :members:
518
+ :undoc-members:
519
+ :show-inheritance:
520
+
521
+ .. autoclass:: DeleteComputer
522
+ :members:
523
+ :undoc-members:
524
+ :show-inheritance:
525
+
526
+ Computer Contract Links
527
+ -----------------------
528
+
529
+ .. autoclass:: GetContractItem
530
+ :members:
531
+ :undoc-members:
532
+ :show-inheritance:
533
+
534
+ .. autoclass:: PostContractItem
535
+ :members:
536
+ :undoc-members:
537
+ :show-inheritance:
538
+
539
+ .. autoclass:: PatchContractItem
540
+ :members:
541
+ :undoc-members:
542
+ :show-inheritance:
543
+
544
+ .. autoclass:: DeleteContractItem
545
+ :members:
546
+ :undoc-members:
547
+ :show-inheritance:
548
+
549
+ Contracts
550
+ ---------
551
+
552
+ .. autoclass:: GetContract
553
+ :members:
554
+ :undoc-members:
555
+ :show-inheritance:
556
+
557
+ .. autoclass:: PostContract
558
+ :members:
559
+ :undoc-members:
560
+ :show-inheritance:
561
+
562
+ .. autoclass:: PatchContract
563
+ :members:
564
+ :undoc-members:
565
+ :show-inheritance:
566
+
567
+ .. autoclass:: DeleteContract
568
+ :members:
569
+ :undoc-members:
570
+ :show-inheritance:
571
+
572
+ Contract Costs
573
+ --------------
574
+
575
+ .. autoclass:: GetContractCost
576
+ :members:
577
+ :undoc-members:
578
+ :show-inheritance:
579
+
580
+ .. autoclass:: PostContractCost
581
+ :members:
582
+ :undoc-members:
583
+ :show-inheritance:
584
+
585
+ .. autoclass:: PatchContractCost
586
+ :members:
587
+ :undoc-members:
588
+ :show-inheritance:
589
+
590
+ .. autoclass:: DeleteContractCost
591
+ :members:
592
+ :undoc-members:
593
+ :show-inheritance:
594
+
595
+ Contract Types
596
+ --------------
597
+
598
+ .. autoclass:: GetContractType
599
+ :members:
600
+ :undoc-members:
601
+ :show-inheritance:
602
+
603
+ .. autoclass:: PostContractType
604
+ :members:
605
+ :undoc-members:
606
+ :show-inheritance:
607
+
608
+ .. autoclass:: PatchContractType
609
+ :members:
610
+ :undoc-members:
611
+ :show-inheritance:
612
+
613
+ .. autoclass:: DeleteContractType
614
+ :members:
615
+ :undoc-members:
616
+ :show-inheritance:
617
+
502
618
  Enums
503
619
  -----
504
620
 
@@ -547,6 +663,11 @@ Enums
547
663
  :undoc-members:
548
664
  :show-inheritance:
549
665
 
666
+ .. autoclass:: GlpiContractRenewalType
667
+ :members:
668
+ :undoc-members:
669
+ :show-inheritance:
670
+
550
671
  Package Metadata
551
672
  ----------------
552
673
 
@@ -5,11 +5,7 @@ from __future__ import annotations
5
5
  from datetime import date
6
6
  from importlib.metadata import PackageNotFoundError, version
7
7
  from pathlib import Path
8
-
9
- try:
10
- from tomllib import loads as toml_loads
11
- except ModuleNotFoundError:
12
- from tomli import loads as toml_loads
8
+ from tomllib import loads as toml_loads
13
9
 
14
10
 
15
11
  def _read_project_version() -> str:
@@ -4,7 +4,7 @@ Installation
4
4
  Requirements
5
5
  ------------
6
6
 
7
- ``glpi-python-client`` supports Python 3.10 and newer. Runtime dependencies are installed
7
+ ``glpi-python-client`` supports Python 3.11 and newer. Runtime dependencies are installed
8
8
  from the package metadata and include ``httpx``, ``tenacity``,
9
9
  ``beautifulsoup4``, ``lxml``, and ``pydantic``.
10
10
 
@@ -92,7 +92,7 @@ GitHub Actions and Read the Docs
92
92
  The repository ships with two GitHub Actions workflows:
93
93
 
94
94
  * ``.github/workflows/ci.yml`` runs on pull requests and pushes to ``main``.
95
- It executes ``pytest`` on Python 3.10 through 3.14, then runs ``ruff``,
95
+ It executes ``pytest`` on Python 3.11 through 3.14, then runs ``ruff``,
96
96
  ``mypy``, and the Sphinx build on Python 3.12.
97
97
  * ``.github/workflows/release.yml`` runs on published GitHub releases. It
98
98
  repeats the quality checks, builds the source and wheel distributions,
@@ -38,7 +38,7 @@ The guide is split into the following sections:
38
38
  throwaway GLPI instance to follow along.
39
39
  4. **GLPI API interface** — the contract-aligned helpers that map
40
40
  one-to-one to GLPI v2 endpoints (tickets, timeline, team members,
41
- users, locations, entities, documents).
41
+ users, locations, entities, documents, computers, contracts).
42
42
  5. **Added functionalities** — helpers built on top of the API mixins:
43
43
  the ``Fields`` plugin custom-field helpers, the aggregated ticket
44
44
  context view, and the reporting helpers.
@@ -580,6 +580,211 @@ Example output::
580
580
  the client (``v1_base_url`` and ``v1_user_token``) because the GLPI v2
581
581
  contract does not advertise a binary upload endpoint.
582
582
 
583
+ Assets
584
+ ~~~~~~
585
+
586
+ The asset mixins map to ``/Assets/*``. GLPI models roughly two dozen
587
+ asset itemtypes (computers, monitors, printers, network equipment, and
588
+ so on), and this client currently implements only ``Computer`` -- there
589
+ is no ``search_monitors`` or ``get_printer``. Treat ``Computer`` as the
590
+ one supported asset type rather than a stand-in for the rest of the
591
+ family.
592
+
593
+ ``search_ / get_ / create_ / update_ / delete_`` follow the same shape
594
+ as the other resources, with ``iter_search_computers`` for streaming
595
+ pagination and the same ``rsql_filter`` / ``limit`` / ``start`` /
596
+ ``sort`` arguments as ``search_tickets``:
597
+
598
+ .. code-block:: python
599
+
600
+ from glpi_python_client import PatchComputer, PostComputer
601
+
602
+ computer_id = client.create_computer(
603
+ PostComputer(name="ws-1042", serial="PF3KL9QJ")
604
+ )
605
+ client.update_computer(
606
+ computer_id, PatchComputer(comment="Reimaged for the finance team")
607
+ )
608
+ computer = client.get_computer(computer_id)
609
+ print(computer.id, computer.name, computer.serial)
610
+
611
+ results = client.search_computers("name==ws-1042", limit=5)
612
+ for c in results:
613
+ print(c.id, c.name)
614
+
615
+ Example output::
616
+
617
+ 1042 ws-1042 PF3KL9QJ
618
+ 1042 ws-1042
619
+
620
+ Linking a computer to a contract
621
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
622
+
623
+ A computer's coverage contracts are tracked as links under
624
+ ``/Assets/Computer/{id}/Contract``, exposed as
625
+ ``list_computer_contracts``, ``get_computer_contract``,
626
+ ``link_computer_contract``, ``update_computer_contract``, and
627
+ ``unlink_computer_contract``:
628
+
629
+ .. code-block:: python
630
+
631
+ from glpi_python_client import IdNameRef, PostContract, PostContractItem
632
+
633
+ contract_id = client.create_contract(PostContract(name="Dell ProSupport 2026"))
634
+
635
+ link_id = client.link_computer_contract(
636
+ computer_id, PostContractItem(contract=IdNameRef(id=contract_id))
637
+ )
638
+ for link in client.list_computer_contracts(computer_id):
639
+ print(link.id, link.itemtype, link.items_id)
640
+
641
+ client.unlink_computer_contract(computer_id, link_id, force=True)
642
+
643
+ Example output::
644
+
645
+ 17 Computer 1042
646
+
647
+ The underlying ``Contract_Item`` GLPI resource is shared by every asset
648
+ type, so ``PostContractItem`` and ``PatchContractItem`` both carry an
649
+ ``itemtype`` field typed as a free string rather than an enum --
650
+ nothing stops a caller from writing ``"Computre"``. Because of that,
651
+ ``link_computer_contract`` and ``update_computer_contract`` ignore
652
+ whatever ``itemtype`` and ``items_id`` are set on the body passed in
653
+ and set both fields themselves from ``computer_id``. Pass only
654
+ ``contract`` (and ``comment``, if the contract ever adds one); a typo
655
+ in the two identifying fields would otherwise attach the link to the
656
+ wrong kind of object with no error from either side.
657
+
658
+ Contracts
659
+ ~~~~~~~~~
660
+
661
+ The contract mixin maps to ``/Management/Contract`` with the usual
662
+ ``search_ / get_ / create_ / update_ / delete_`` shape and
663
+ ``iter_search_contracts`` for streaming pagination:
664
+
665
+ .. code-block:: python
666
+
667
+ from glpi_python_client import PatchContract, PostContract
668
+
669
+ contract_id = client.create_contract(
670
+ PostContract(name="Dell ProSupport 2026", number="CTR-2026-001")
671
+ )
672
+ client.update_contract(
673
+ contract_id, PatchContract(comment="Renewed for another year")
674
+ )
675
+ contract = client.get_contract(contract_id)
676
+ print(contract.id, contract.name, contract.date_begin)
677
+
678
+ results = client.search_contracts("name==Dell ProSupport 2026", limit=5)
679
+ for c in results:
680
+ print(c.id, c.name)
681
+
682
+ Example output::
683
+
684
+ 501 Dell ProSupport 2026 2026-01-15
685
+ 501 Dell ProSupport 2026
686
+
687
+ .. warning::
688
+
689
+ ``date_begin`` on :class:`~glpi_python_client.GetContract` and
690
+ :class:`~glpi_python_client.PostContract` is a plain
691
+ :class:`datetime.date`, not a :class:`~datetime.datetime`. The GLPI
692
+ contract declares this field with ``format: date`` -- a contract has
693
+ no time-of-day for its start. That choice is load-bearing, not
694
+ cosmetic: the server-clock conversion in ``models/_base.py`` that
695
+ rewrites timestamps into the configured ``server_timezone`` only
696
+ touches values that are instances of :class:`datetime.datetime`, and
697
+ a plain :class:`~datetime.date` is not one, so ``date_begin`` never
698
+ enters that conversion. Had it been modelled as ``datetime`` it would
699
+ have been eligible, and converting a midnight, offset-naive value
700
+ between timezones can roll it onto the previous or next calendar
701
+ day. Note the asymmetry: ``date_begin`` and ``date_end`` on
702
+ ``ContractCost`` (below) *are* ``datetime`` fields, because the
703
+ contract declares those two with ``format: date-time``. That
704
+ difference comes from the GLPI contract itself and is not an
705
+ inconsistency to reconcile.
706
+
707
+ Contract costs
708
+ ^^^^^^^^^^^^^^
709
+
710
+ Cost lines live under ``/Management/Contract/{id}/Cost`` as a genuine
711
+ sub-resource with their own create, update, and delete endpoints:
712
+ ``list_contract_costs``, ``get_contract_cost``,
713
+ ``create_contract_cost``, ``update_contract_cost``, and
714
+ ``delete_contract_cost``.
715
+
716
+ .. code-block:: python
717
+
718
+ from glpi_python_client import PatchContractCost, PostContractCost
719
+
720
+ cost_id = client.create_contract_cost(
721
+ contract_id, PostContractCost(name="Year 1", cost=4200.0)
722
+ )
723
+ client.update_contract_cost(
724
+ contract_id, cost_id, PatchContractCost(comment="Paid on invoice #88")
725
+ )
726
+ cost = client.get_contract_cost(contract_id, cost_id)
727
+ print(cost.id, cost.name, cost.cost)
728
+
729
+ print(len(client.list_contract_costs(contract_id)))
730
+
731
+ client.delete_contract_cost(contract_id, cost_id, force=True)
732
+
733
+ Example output::
734
+
735
+ 9 Year 1 4200.0
736
+ 1
737
+
738
+ .. note::
739
+
740
+ ``costs`` on :class:`~glpi_python_client.GetContract` is read-only:
741
+ it comes back populated with references to the contract's cost
742
+ lines, but the field does not exist at all on ``PostContract`` or
743
+ ``PatchContract``. Write cost lines through
744
+ ``create_contract_cost``, ``update_contract_cost``, and
745
+ ``delete_contract_cost`` instead of trying to assign ``costs`` on
746
+ the parent contract.
747
+
748
+ Contract types
749
+ ^^^^^^^^^^^^^^
750
+
751
+ ``/Dropdowns/ContractType`` is a plain dropdown: ``search_ / get_ /
752
+ create_ / update_ / delete_`` plus ``iter_search_contract_types``.
753
+ Unlike ``search_computers`` and ``search_contracts``, the contract-type
754
+ search helpers do not accept a ``sort`` argument.
755
+
756
+ .. code-block:: python
757
+
758
+ from glpi_python_client import PostContractType
759
+
760
+ type_id = client.create_contract_type(PostContractType(name="Maintenance"))
761
+ contract_type = client.get_contract_type(type_id)
762
+ print(contract_type.id, contract_type.name)
763
+
764
+ Example output::
765
+
766
+ 6 Maintenance
767
+
768
+ Assign the type -- and a renewal behaviour -- through the parent
769
+ contract:
770
+
771
+ .. code-block:: python
772
+
773
+ from glpi_python_client import GlpiContractRenewalType, IdNameRef, PatchContract
774
+
775
+ client.update_contract(
776
+ contract_id,
777
+ PatchContract(
778
+ type=IdNameRef(id=type_id),
779
+ renewal_type=GlpiContractRenewalType.TACIT,
780
+ ),
781
+ )
782
+
783
+ :class:`glpi_python_client.GlpiContractRenewalType` mirrors the three
784
+ values GLPI documents for ``Contract.renewal_type``: ``NONE`` (no
785
+ renewal), ``TACIT`` (automatic renewal), and ``EXPLICIT`` (manual
786
+ renewal).
787
+
583
788
  Knowledge base
584
789
  ~~~~~~~~~~~~~~
585
790
 
@@ -685,8 +890,9 @@ the package root for easy use in RSQL filters:
685
890
  :class:`glpi_python_client.GlpiTaskState`,
686
891
  :class:`glpi_python_client.GlpiSolutionStatus`,
687
892
  :class:`glpi_python_client.GlpiTimelinePosition`,
688
- :class:`glpi_python_client.GlpiUserAuthType`, and
689
- :class:`glpi_python_client.GlpiGlobalValidation`.
893
+ :class:`glpi_python_client.GlpiUserAuthType`,
894
+ :class:`glpi_python_client.GlpiGlobalValidation`, and
895
+ :class:`glpi_python_client.GlpiContractRenewalType`.
690
896
 
691
897
  .. code-block:: python
692
898
 
@@ -1552,6 +1758,36 @@ The whole page is built in one pass, so a single unconvertible record used
1552
1758
  to make its page-mates unreadable too. The failure is now scoped to the
1553
1759
  record whose body you actually read.
1554
1760
 
1761
+ The Markdown is CommonMark with GFM tables. Rendering it -- as the package
1762
+ does on the way back to GLPI, with cmark-gfm -- displays what GLPI
1763
+ displayed, and reading that rendering back gives the same Markdown. Text in
1764
+ a body is literal: Markdown has one spelling for ``__init__`` typed by a
1765
+ user and for bold ``init``, so text that would read as syntax is escaped:
1766
+
1767
+ .. code-block:: python
1768
+
1769
+ from glpi_python_client.content import GlpiContentConverter
1770
+
1771
+ GlpiContentConverter.from_transport(
1772
+ r"<p>Voir __init__ et \\serveur\compta</p><p># pas un titre</p>"
1773
+ )
1774
+ # Voir \_\_init\_\_ et \\\serveur\compta
1775
+ #
1776
+ # \# pas un titre
1777
+
1778
+ Ordinary prose stays as typed: ``fichier_de_test_v2.xlsx``, ``C:\Temp``,
1779
+ ``R&D``, a ``#`` mid-sentence. The spelling is canonical: a line break reads
1780
+ back as ``\`` and a newline, a nested list is indented by its bullet's width,
1781
+ and a table comes back unpadded. A body with no HTML element is plain text
1782
+ and is read as GLPI shows it, its lines as lines.
1783
+
1784
+ Writing, your Markdown is rendered by cmark-gfm. A newline is a line break,
1785
+ GFM tables work, and raw HTML passes through, so put a placeholder such as
1786
+ ``<login>`` in backticks. A write model keeps your Markdown verbatim unless
1787
+ it starts with an HTML tag, in which case it is read as HTML. A Markdown
1788
+ table needs a header row, so a header-less HTML table reads back with an
1789
+ empty one, and struck-through text stays as raw ``<s>``.
1790
+
1555
1791
  .. note::
1556
1792
 
1557
1793
  Deeply nested HTML is the case worth knowing about. The HTML-to-Markdown