glpi-python-client 0.6.3__tar.gz → 0.6.5__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 (172) hide show
  1. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/CHANGELOG.md +52 -0
  2. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/PKG-INFO +1 -1
  3. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/api_reference.rst +10 -0
  4. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/user_guide.rst +58 -1
  5. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/__init__.py +5 -1
  6. glpi_python_client-0.6.5/glpi_python_client/_async/clients/api/assistance/timeline/_document.py +410 -0
  7. glpi_python_client-0.6.5/glpi_python_client/_sync/clients/api/assistance/timeline/_document.py +410 -0
  8. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/content/conversion.py +59 -0
  9. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/__init__.py +4 -0
  10. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/enums.py +5 -0
  11. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/management/__init__.py +6 -0
  12. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/management/_document.py +14 -0
  13. glpi_python_client-0.6.5/glpi_python_client/models/api_schema/management/_document_item.py +88 -0
  14. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/pyproject.toml +1 -1
  15. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-asset-workflow/SKILL.md +1 -1
  16. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-client-setup/SKILL.md +1 -1
  17. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-contract-workflow/SKILL.md +1 -1
  18. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-document-workflow/SKILL.md +3 -2
  19. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-knowledge-base/SKILL.md +1 -1
  20. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-plugin-fields/SKILL.md +1 -1
  21. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-reporting-and-context/SKILL.md +1 -1
  22. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-team-members/SKILL.md +1 -1
  23. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-ticket-timeline/SKILL.md +2 -1
  24. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-ticket-workflow/SKILL.md +1 -1
  25. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/glpi-user-location-provisioning/SKILL.md +1 -1
  26. glpi_python_client-0.6.3/glpi_python_client/_async/clients/api/assistance/timeline/_document.py +0 -225
  27. glpi_python_client-0.6.3/glpi_python_client/_sync/clients/api/assistance/timeline/_document.py +0 -225
  28. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/.gitignore +0 -0
  29. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/.pre-commit-config.yaml +0 -0
  30. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/.readthedocs.yaml +0 -0
  31. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/CONTRIBUTING.md +0 -0
  32. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/LICENSE +0 -0
  33. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/README.md +0 -0
  34. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/_static/.gitkeep +0 -0
  35. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/conf.py +0 -0
  36. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/development.md +0 -0
  37. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/development_rtd.rst +0 -0
  38. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/index.rst +0 -0
  39. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/installation.rst +0 -0
  40. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/publishing.md +0 -0
  41. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/publishing_rtd.rst +0 -0
  42. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/docs/sponsoring.rst +0 -0
  43. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/__init__.py +0 -0
  44. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/_concurrency.py +0 -0
  45. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/_testing.py +0 -0
  46. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/auth/__init__.py +0 -0
  47. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/auth/_v1_session.py +0 -0
  48. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/auth/auth.py +0 -0
  49. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/__init__.py +0 -0
  50. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/_base_client.py +0 -0
  51. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/__init__.py +0 -0
  52. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/administration/__init__.py +0 -0
  53. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/administration/_entity.py +0 -0
  54. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/administration/_user.py +0 -0
  55. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/assets/__init__.py +0 -0
  56. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/assets/_computer.py +0 -0
  57. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/assistance/__init__.py +0 -0
  58. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/assistance/_team.py +0 -0
  59. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/assistance/_ticket.py +0 -0
  60. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/assistance/timeline/__init__.py +0 -0
  61. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/assistance/timeline/_followup.py +0 -0
  62. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/assistance/timeline/_solution.py +0 -0
  63. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/assistance/timeline/_task.py +0 -0
  64. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/dropdowns/__init__.py +0 -0
  65. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/dropdowns/_contract_type.py +0 -0
  66. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/dropdowns/_location.py +0 -0
  67. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/knowledgebase/__init__.py +0 -0
  68. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/knowledgebase/_article.py +0 -0
  69. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/knowledgebase/_category.py +0 -0
  70. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/knowledgebase/_comment.py +0 -0
  71. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/knowledgebase/_revision.py +0 -0
  72. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/management/__init__.py +0 -0
  73. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/management/_contract.py +0 -0
  74. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/management/_document.py +0 -0
  75. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/plugins/__init__.py +0 -0
  76. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/api/plugins/_fields.py +0 -0
  77. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/client.py +0 -0
  78. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/commons/__init__.py +0 -0
  79. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/commons/_config.py +0 -0
  80. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/commons/_constants.py +0 -0
  81. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/commons/_filters.py +0 -0
  82. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/commons/_http.py +0 -0
  83. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/commons/_payloads.py +0 -0
  84. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/commons/_transport.py +0 -0
  85. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/custom/__init__.py +0 -0
  86. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/custom/_statistics.py +0 -0
  87. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_async/clients/custom/_ticket_context.py +0 -0
  88. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_errors.py +0 -0
  89. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/__init__.py +0 -0
  90. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/_concurrency.py +0 -0
  91. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/_testing.py +0 -0
  92. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/auth/__init__.py +0 -0
  93. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/auth/_v1_session.py +0 -0
  94. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/auth/auth.py +0 -0
  95. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/__init__.py +0 -0
  96. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/_base_client.py +0 -0
  97. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/__init__.py +0 -0
  98. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/administration/__init__.py +0 -0
  99. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/administration/_entity.py +0 -0
  100. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/administration/_user.py +0 -0
  101. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/assets/__init__.py +0 -0
  102. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/assets/_computer.py +0 -0
  103. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/assistance/__init__.py +0 -0
  104. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/assistance/_team.py +0 -0
  105. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/assistance/_ticket.py +0 -0
  106. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/assistance/timeline/__init__.py +0 -0
  107. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/assistance/timeline/_followup.py +0 -0
  108. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/assistance/timeline/_solution.py +0 -0
  109. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/assistance/timeline/_task.py +0 -0
  110. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/dropdowns/__init__.py +0 -0
  111. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/dropdowns/_contract_type.py +0 -0
  112. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/dropdowns/_location.py +0 -0
  113. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/knowledgebase/__init__.py +0 -0
  114. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/knowledgebase/_article.py +0 -0
  115. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/knowledgebase/_category.py +0 -0
  116. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/knowledgebase/_comment.py +0 -0
  117. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/knowledgebase/_revision.py +0 -0
  118. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/management/__init__.py +0 -0
  119. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/management/_contract.py +0 -0
  120. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/management/_document.py +0 -0
  121. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/plugins/__init__.py +0 -0
  122. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/api/plugins/_fields.py +0 -0
  123. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/client.py +0 -0
  124. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/commons/__init__.py +0 -0
  125. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/commons/_config.py +0 -0
  126. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/commons/_constants.py +0 -0
  127. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/commons/_filters.py +0 -0
  128. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/commons/_http.py +0 -0
  129. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/commons/_payloads.py +0 -0
  130. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/commons/_transport.py +0 -0
  131. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/custom/__init__.py +0 -0
  132. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/custom/_statistics.py +0 -0
  133. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/_sync/clients/custom/_ticket_context.py +0 -0
  134. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/content/__init__.py +0 -0
  135. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/_base.py +0 -0
  136. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/__init__.py +0 -0
  137. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/_common.py +0 -0
  138. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/_content.py +0 -0
  139. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/administration/__init__.py +0 -0
  140. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/administration/_entity.py +0 -0
  141. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/administration/_user.py +0 -0
  142. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assets/__init__.py +0 -0
  143. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assets/_computer.py +0 -0
  144. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assets/_contract_item.py +0 -0
  145. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assistance/__init__.py +0 -0
  146. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assistance/_team.py +0 -0
  147. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assistance/_ticket.py +0 -0
  148. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assistance/timeline/__init__.py +0 -0
  149. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assistance/timeline/_document.py +0 -0
  150. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assistance/timeline/_followup.py +0 -0
  151. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assistance/timeline/_solution.py +0 -0
  152. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/assistance/timeline/_task.py +0 -0
  153. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/dropdowns/__init__.py +0 -0
  154. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/dropdowns/_contract_type.py +0 -0
  155. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/dropdowns/_location.py +0 -0
  156. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/knowledgebase/__init__.py +0 -0
  157. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/knowledgebase/_article.py +0 -0
  158. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/knowledgebase/_category.py +0 -0
  159. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/knowledgebase/_comment.py +0 -0
  160. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/knowledgebase/_revision.py +0 -0
  161. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/management/_contract.py +0 -0
  162. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/management/_contract_cost.py +0 -0
  163. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/plugins/__init__.py +0 -0
  164. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/api_schema/plugins/_fields.py +0 -0
  165. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/custom_schema/__init__.py +0 -0
  166. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/models/custom_schema/_ticket_context.py +0 -0
  167. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/py.typed +0 -0
  168. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/rsql.py +0 -0
  169. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/testing/__init__.py +0 -0
  170. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/testing/fixtures.py +0 -0
  171. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/glpi_python_client/testing/utils.py +0 -0
  172. {glpi_python_client-0.6.3 → glpi_python_client-0.6.5}/skills/README.md +0 -0
@@ -4,6 +4,58 @@ 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.5 — 2026-10-14
8
+
9
+ A patch release that lets a caller find the Document behind an image GLPI left
10
+ as a raw `data:` URI. GLPI may keep a large pasted image in the stored body as
11
+ `<img id="..." src="data:image/png;base64,...">` instead of rewriting it into
12
+ a document link, while still creating its Document, whose tag equals the
13
+ image's `id`. Existing methods are unchanged.
14
+
15
+ ### Added
16
+
17
+ - **`Link.image_id`**, a new last field of the `Link` the `rewrite_link`
18
+ callback receives: for an `<img>`, the value of its `id` attribute (trimmed;
19
+ `None` when absent or blank); always `None` for a link.
20
+ - **`GlpiContentConverter.data_image_ids(html)`** returns, in document order
21
+ and without duplicates, the `id` of every `<img>` whose `src` is a `data:`
22
+ URI, so a caller can tell whether it needs to read any Document at all. It
23
+ does not decode the base64, and leaves out an image inside `<pre>`, `<code>`,
24
+ `<kbd>` or `<samp>`, which the reader never offers to `rewrite_link`.
25
+ - **`GetDocument.tag`**, a read-only property: the Document's tag from
26
+ `extra_payload`, or `None` when absent or not a non-empty string. GLPI sets a
27
+ tag on the Documents it creates from an upload or an editor paste, images or
28
+ not.
29
+
30
+ ## 0.6.4 — 2026-10-12
31
+
32
+ A patch release that reads the documents GLPI links to a followup. GLPI's
33
+ editor links an image pasted into a followup to the followup and not to the
34
+ ticket, so the ticket-level listing never returned it. Existing methods are
35
+ unchanged.
36
+
37
+ ### Added
38
+
39
+ - **`list_item_documents(itemtype, items_id)`** lists the v1 `Document_Item`
40
+ links of one item as `GetDocumentItem` rows. It needs the v1 session
41
+ (`v1_base_url` and `v1_user_token`) and raises `RuntimeError` without one.
42
+ An invalid `itemtype` or id, a non-list answer, or a full page of 1000 rows
43
+ or more raises `GlpiValidationError`; a partial list is never returned.
44
+ - **`GetDocumentItem`**, exported from the package root, models a link row:
45
+ `id`, `documents_id`, `itemtype`, `items_id`, `entities_id`, the dates and
46
+ `timeline_position`. Other keys stay in `extra_payload`.
47
+ - **`list_ticket_followup_documents(ticket_id, *, include_private=False)`**
48
+ returns one `FollowupDocuments` (the followup and its `GetDocumentItem`
49
+ links) per followup, in ascending followup id, empty ones included. Private
50
+ followups, and followups with no `is_private` flag, are dropped by default;
51
+ `include_private=True` keeps every followup.
52
+ - **`FollowupDocuments`**, exported from the package root.
53
+ - **`GlpiTimelinePosition.NO_TIMELINE`** (-1) is an alias of `INVALID`, so
54
+ existing uses of `INVALID` are unaffected.
55
+ - The followup route was measured live. The primitive `list_item_documents`
56
+ does not filter private followups; the convenience filters them by default.
57
+ Tasks and solutions are not measured, and no helper walks them.
58
+
7
59
  ## 0.6.3 — 2026-10-08
8
60
 
9
61
  A patch release of the converter's reading: a caller can recognise the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: glpi-python-client
3
- Version: 0.6.3
3
+ Version: 0.6.5
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/
@@ -315,6 +315,16 @@ Documents
315
315
  :undoc-members:
316
316
  :show-inheritance:
317
317
 
318
+ .. autoclass:: GetDocumentItem
319
+ :members:
320
+ :undoc-members:
321
+ :show-inheritance:
322
+
323
+ .. autoclass:: FollowupDocuments
324
+ :members:
325
+ :undoc-members:
326
+ :show-inheritance:
327
+
318
328
  .. autoclass:: PostDocument
319
329
  :members:
320
330
  :undoc-members:
@@ -380,7 +380,8 @@ The full set is re-exported from the package root, including
380
380
  ``GetEntity`` / ``PostEntity`` / ``PatchEntity`` / ``DeleteEntity``,
381
381
  ``GetFollowup``, ``GetTicketTask``, ``GetSolution``, ``GetTimelineDocument``,
382
382
  ``GetTeamMember``, and ``GetDocument`` together with their post / patch /
383
- delete variants.
383
+ delete variants. The read-only ``GetDocumentItem`` and ``FollowupDocuments``
384
+ have no post / patch / delete variants.
384
385
 
385
386
  All models inherit from a permissive base: the GLPI server is the
386
387
  authoritative validator, so any extra keys returned by the live server
@@ -580,6 +581,43 @@ Example output::
580
581
  the client (``v1_base_url`` and ``v1_user_token``) because the GLPI v2
581
582
  contract does not advertise a binary upload endpoint.
582
583
 
584
+ Documents linked to a followup
585
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
586
+
587
+ GLPI's editor links an image pasted into a followup to the followup, not to
588
+ the ticket, so ``list_ticket_timeline_documents`` never returns it. Two
589
+ helpers read those links through the legacy v1 session (``v1_base_url`` and
590
+ ``v1_user_token``); without one they raise ``RuntimeError``.
591
+
592
+ ``list_item_documents(itemtype, items_id)`` returns the document links of any
593
+ item as :class:`~glpi_python_client.GetDocumentItem` rows: the link ``id``, the
594
+ ``documents_id``, the ``itemtype`` and ``items_id`` it names, and a
595
+ ``timeline_position``. It does not filter anything, private followups
596
+ included. A page of 1000 rows or more raises ``GlpiValidationError`` rather
597
+ than return a possibly truncated list.
598
+
599
+ ``list_ticket_followup_documents(ticket_id, *, include_private=False)`` walks
600
+ the ticket's followups and returns one
601
+ :class:`~glpi_python_client.FollowupDocuments` per followup, in ascending
602
+ followup id, holding the followup and its links (an empty list for a followup
603
+ without any). By default private followups, and followups whose ``is_private``
604
+ flag is missing, are dropped and not queried; pass ``include_private=True`` to
605
+ keep them all.
606
+
607
+ .. code-block:: python
608
+
609
+ for entry in client.list_ticket_followup_documents(ticket_id):
610
+ for link in entry.documents:
611
+ print(entry.followup.id, link.documents_id, link.timeline_position)
612
+
613
+ ``timeline_position`` is a :class:`~glpi_python_client.GlpiTimelinePosition`.
614
+ ``GlpiTimelinePosition.NO_TIMELINE`` (-1) names the value GLPI gives a link
615
+ that sits outside the ticket timeline, such as an image pasted into a
616
+ followup. It is an alias of ``INVALID``, which keeps its meaning.
617
+
618
+ The followup route was measured against a live server. Tasks and solutions
619
+ were not measured, so no helper walks them.
620
+
583
621
  Assets
584
622
  ~~~~~~
585
623
 
@@ -1969,6 +2007,25 @@ other type raises ``TypeError``. It is not called for a value passed through
1969
2007
  as Markdown (``plain_text_is_markdown=True``), nor for a body read as its text.
1970
2008
  ``easyvista-python-client`` 0.4.3 carries the same hook.
1971
2009
 
2010
+ GLPI does not always rewrite a pasted image into a document link. A large one
2011
+ can stay in the stored body as ``<img id="tag-1" src="data:image/png;base64,...">``
2012
+ while GLPI still creates its Document, whose tag is the image's ``id``. For
2013
+ such an image ``Link.image_id`` is that ``id`` and ``href`` is the data URI;
2014
+ ``GlpiContentConverter.data_image_ids(html)`` lists those ids without
2015
+ converting, so a caller reads Documents only when a body needs it. A
2016
+ :class:`~glpi_python_client.models.GetDocument` exposes its tag as ``tag``:
2017
+
2018
+ .. code-block:: python
2019
+
2020
+ ids = GlpiContentConverter.data_image_ids(body)
2021
+ if ids:
2022
+ by_tag = {d.tag: d for d in documents if d.tag}
2023
+ matched = [by_tag[i] for i in ids if i in by_tag]
2024
+
2025
+ The ``id`` is body text, and anyone who can write the body can forge it. Match
2026
+ it only against Documents the caller already trusts, such as the same
2027
+ ticket's own, never against every Document the instance holds.
2028
+
1972
2029
  It is not a sanitiser
1973
2030
  ^^^^^^^^^^^^^^^^^^^^^
1974
2031
 
@@ -50,12 +50,14 @@ from glpi_python_client.models import (
50
50
  DeleteTicketTask,
51
51
  DeleteTimelineDocument,
52
52
  DeleteUser,
53
+ FollowupDocuments,
53
54
  GetComputer,
54
55
  GetContract,
55
56
  GetContractCost,
56
57
  GetContractItem,
57
58
  GetContractType,
58
59
  GetDocument,
60
+ GetDocumentItem,
59
61
  GetEntity,
60
62
  GetFollowup,
61
63
  GetKBArticle,
@@ -131,7 +133,7 @@ from glpi_python_client.rsql import (
131
133
  date_window,
132
134
  )
133
135
 
134
- __version__ = "0.6.3"
136
+ __version__ = "0.6.5"
135
137
 
136
138
  __all__ = [
137
139
  "AsyncGlpiClient",
@@ -153,12 +155,14 @@ __all__ = [
153
155
  "DeleteTicketTask",
154
156
  "DeleteTimelineDocument",
155
157
  "DeleteUser",
158
+ "FollowupDocuments",
156
159
  "GetComputer",
157
160
  "GetContract",
158
161
  "GetContractCost",
159
162
  "GetContractItem",
160
163
  "GetContractType",
161
164
  "GetDocument",
165
+ "GetDocumentItem",
162
166
  "GetEntity",
163
167
  "GetFollowup",
164
168
  "GetKBArticle",
@@ -0,0 +1,410 @@
1
+ """GLPI ``/Assistance/Ticket/{id}/Timeline/Document`` mixin.
2
+
3
+ The mixin exposes list, fetch, link, and unlink helpers for the timeline
4
+ document endpoint that links existing GLPI documents to a ticket.
5
+
6
+ Notes
7
+ -----
8
+ The live GLPI v2 server returns each entry of the list endpoint wrapped
9
+ in a ``{"type": "Document_Item", "item": {...}}`` envelope, even though
10
+ the OpenAPI contract documents a flat array of ``Document_Item``. The
11
+ ``item`` value is a full ``Document`` record (matching :class:`GetDocument`),
12
+ not a ``Document_Item`` link record — real behaviour wins over the contract.
13
+ :func:`list_ticket_timeline_documents` unwraps the envelope through the shared
14
+ ``TransportMixin._resource_list`` helper and deserialises each inner object
15
+ as :class:`GetDocument`.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import re
21
+
22
+ from glpi_python_client._async._concurrency import gather
23
+ from glpi_python_client._async.clients.commons._constants import (
24
+ TICKET_ENDPOINT,
25
+ TIMELINE_DOCUMENT_SUFFIX,
26
+ GlpiId,
27
+ )
28
+ from glpi_python_client._async.clients.commons._payloads import model_from_payload
29
+ from glpi_python_client._async.clients.commons._transport import TransportMixin
30
+ from glpi_python_client._errors import GlpiValidationError
31
+ from glpi_python_client.models.api_schema.assistance.timeline._document import (
32
+ DeleteTimelineDocument,
33
+ PatchTimelineDocument,
34
+ PostTimelineDocument,
35
+ )
36
+ from glpi_python_client.models.api_schema.assistance.timeline._followup import (
37
+ GetFollowup,
38
+ )
39
+ from glpi_python_client.models.api_schema.management._document import GetDocument
40
+ from glpi_python_client.models.api_schema.management._document_item import (
41
+ FollowupDocuments,
42
+ GetDocumentItem,
43
+ )
44
+
45
+ _ITEM_DOCUMENT_RANGE = "0-999"
46
+ _ITEM_DOCUMENT_PAGE_SIZE = 1000
47
+ _ITEMTYPE_PATTERN = re.compile(r"^[A-Za-z][A-Za-z0-9_]*$")
48
+ _V1_FEATURE_LABEL = "item document link listing"
49
+ _FOLLOWUP_ITEMTYPE = "ITILFollowup"
50
+
51
+
52
+ class TimelineDocumentMixin(TransportMixin):
53
+ """CRUD helpers for the ticket document timeline endpoint."""
54
+
55
+ async def list_ticket_timeline_documents(
56
+ self, ticket_id: GlpiId
57
+ ) -> list[GetDocument]:
58
+ """List all documents linked to one ticket timeline.
59
+
60
+ Parameters
61
+ ----------
62
+ ticket_id : GlpiId
63
+ Numeric identifier of the parent ticket.
64
+
65
+ Returns
66
+ -------
67
+ list[GetDocument]
68
+ Document records returned by the GLPI server. The live API
69
+ wraps each entry in a ``{"type": "Document_Item", "item": {...}}``
70
+ envelope whose ``item`` value is a full ``Document`` record; the
71
+ envelope is unwrapped automatically.
72
+ """
73
+
74
+ return await self._resource_list(
75
+ f"{TICKET_ENDPOINT}/{ticket_id}/{TIMELINE_DOCUMENT_SUFFIX}",
76
+ GetDocument,
77
+ failure_message=(
78
+ f"Failed to list timeline documents for ticket {ticket_id}"
79
+ ),
80
+ unwrap_envelope=True,
81
+ )
82
+
83
+ async def list_item_documents(
84
+ self, itemtype: str, items_id: GlpiId
85
+ ) -> list[GetDocumentItem]:
86
+ """List the document links of one item, over the legacy v1 API.
87
+
88
+ Only the followup route (``itemtype="ITILFollowup"``) has been
89
+ checked against a live server. Other itemtypes are sent to the same
90
+ v1 sub-route unchanged.
91
+
92
+ Unlike :meth:`list_ticket_timeline_documents`, this call needs the
93
+ legacy v1 session (``v1_base_url`` and ``v1_user_token``). Without
94
+ one it raises ``RuntimeError``.
95
+
96
+ It returns link rows, not documents. A caller resolves each
97
+ ``documents_id`` with ``get_document``.
98
+
99
+ The library does not filter private followups, tasks or solutions
100
+ for the caller: every link the authenticated user can read is
101
+ returned.
102
+
103
+ GLPI's editor links an image pasted into a followup to the followup,
104
+ not to the ticket. A caller that wants such images therefore walks
105
+ the ticket's followups and lists each one.
106
+
107
+ Parameters
108
+ ----------
109
+ itemtype : str
110
+ GLPI itemtype owning the links, for example ``"ITILFollowup"``.
111
+ It must start with a letter and contain only letters, digits and
112
+ underscores, because it is placed in the URL path.
113
+ items_id : GlpiId
114
+ Positive numeric identifier of the item.
115
+
116
+ Returns
117
+ -------
118
+ list[GetDocumentItem]
119
+ Link rows, with naive timestamps localised to the server
120
+ timezone. An empty answer gives an empty list.
121
+
122
+ Raises
123
+ ------
124
+ RuntimeError
125
+ If the client was built without a v1 session.
126
+ GlpiValidationError
127
+ If ``itemtype`` or ``items_id`` is invalid, if GLPI answers
128
+ something other than a list or an empty body, or if it answers a
129
+ full page of 1000 rows or more, which may be truncated. A
130
+ partial list is never returned.
131
+ GlpiStatusError
132
+ If the GLPI server returns a non-success HTTP status.
133
+ """
134
+
135
+ v1 = self._require_v1_session(_V1_FEATURE_LABEL)
136
+ if not _ITEMTYPE_PATTERN.fullmatch(itemtype):
137
+ raise GlpiValidationError(
138
+ f"itemtype must match {_ITEMTYPE_PATTERN.pattern!r}, got {itemtype!r}"
139
+ )
140
+ if items_id <= 0:
141
+ raise GlpiValidationError(
142
+ f"items_id must be a positive integer, got {items_id!r}"
143
+ )
144
+ payload = await v1.request_json(
145
+ "GET",
146
+ f"{itemtype}/{items_id}/Document_Item",
147
+ params={"range": _ITEM_DOCUMENT_RANGE},
148
+ failure_message=(f"Failed to list document links of {itemtype} {items_id}"),
149
+ )
150
+ if isinstance(payload, dict) and not payload:
151
+ return []
152
+ if not isinstance(payload, list):
153
+ raise GlpiValidationError(
154
+ f"Unexpected document link answer for {itemtype} {items_id}: "
155
+ f"{type(payload).__name__}"
156
+ )
157
+ if len(payload) >= _ITEM_DOCUMENT_PAGE_SIZE:
158
+ raise GlpiValidationError(
159
+ f"{itemtype} {items_id} returned {len(payload)} document links, "
160
+ "a full page that may be truncated"
161
+ )
162
+ return [
163
+ model_from_payload(
164
+ GetDocumentItem, row, server_timezone=self.server_timezone
165
+ )
166
+ for row in payload
167
+ ]
168
+
169
+ async def list_ticket_followup_documents(
170
+ self, ticket_id: GlpiId, *, include_private: bool = False
171
+ ) -> list[FollowupDocuments]:
172
+ """List the document links of each followup of one ticket.
173
+
174
+ GLPI's editor links an image pasted into a followup to the followup,
175
+ not to the ticket. :meth:`list_ticket_timeline_documents` therefore
176
+ never returns such an image, and this method does.
177
+
178
+ Each entry holds the followup and its links, so a caller reads
179
+ ``followup.is_private`` to classify every link without a second
180
+ read. Only followups are walked: tasks and solutions are not.
181
+
182
+ This call needs the legacy v1 session (``v1_base_url`` and
183
+ ``v1_user_token``). Without one it raises ``RuntimeError``. It issues
184
+ one v1 request per kept followup, all at once on the asynchronous
185
+ surface and one after the other on the synchronous one.
186
+
187
+ Parameters
188
+ ----------
189
+ ticket_id : GlpiId
190
+ Numeric identifier of the parent ticket.
191
+ include_private : bool, optional
192
+ When false (the default), private followups and followups whose
193
+ ``is_private`` flag is missing are dropped and not queried, so
194
+ only what a requester can see is returned. A missing flag counts
195
+ as private. When true, every followup is kept.
196
+
197
+ Returns
198
+ -------
199
+ list[FollowupDocuments]
200
+ One entry per kept followup, in ascending followup id, including
201
+ a followup with no link (an empty ``documents`` list). A link row
202
+ that does not name that followup as its item is dropped.
203
+
204
+ Raises
205
+ ------
206
+ RuntimeError
207
+ If the client was built without a v1 session.
208
+ GlpiValidationError
209
+ If a kept followup has no identifier, or if reading the links of
210
+ a followup fails validation. A partial list is never returned.
211
+ GlpiStatusError
212
+ If the GLPI server returns a non-success HTTP status.
213
+ """
214
+
215
+ self._require_v1_session(_V1_FEATURE_LABEL)
216
+ followups = await self.list_ticket_followups(ticket_id) # type: ignore[attr-defined]
217
+ kept = [
218
+ followup
219
+ for followup in followups
220
+ if include_private or followup.is_private is False
221
+ ]
222
+ keyed: list[tuple[int, GetFollowup]] = []
223
+ for followup in kept:
224
+ if followup.id is None:
225
+ raise GlpiValidationError(
226
+ f"A followup of ticket {ticket_id} has no id, "
227
+ "so its document links cannot be read"
228
+ )
229
+ keyed.append((followup.id, followup))
230
+ keyed.sort(key=lambda pair: pair[0])
231
+ link_lists = await gather(
232
+ *(
233
+ self.list_item_documents(_FOLLOWUP_ITEMTYPE, followup_id)
234
+ for followup_id, _ in keyed
235
+ )
236
+ )
237
+ return [
238
+ FollowupDocuments(
239
+ followup=followup,
240
+ documents=[
241
+ row
242
+ for row in rows
243
+ if row.itemtype == _FOLLOWUP_ITEMTYPE
244
+ and row.items_id == followup_id
245
+ ],
246
+ )
247
+ for (followup_id, followup), rows in zip(keyed, link_lists, strict=True)
248
+ ]
249
+
250
+ async def get_ticket_timeline_document(
251
+ self, ticket_id: GlpiId, document_link_id: GlpiId
252
+ ) -> GetDocument:
253
+ """Fetch one document linked to the ticket timeline by its document ID.
254
+
255
+ Parameters
256
+ ----------
257
+ ticket_id : GlpiId
258
+ Numeric identifier of the parent ticket.
259
+ document_link_id : GlpiId
260
+ Numeric identifier of the linked document to retrieve.
261
+
262
+ Returns
263
+ -------
264
+ GetDocument
265
+ Validated document payload.
266
+
267
+ Raises
268
+ ------
269
+ GlpiStatusError
270
+ If the GLPI server returns a non-success HTTP status.
271
+ """
272
+
273
+ return await self._resource_get(
274
+ f"{TICKET_ENDPOINT}/{ticket_id}/"
275
+ f"{TIMELINE_DOCUMENT_SUFFIX}/{document_link_id}",
276
+ GetDocument,
277
+ failure_message=(
278
+ f"Failed to get timeline document {document_link_id} on "
279
+ f"ticket {ticket_id}"
280
+ ),
281
+ )
282
+
283
+ async def link_ticket_timeline_document(
284
+ self, ticket_id: GlpiId, document_link: PostTimelineDocument
285
+ ) -> int:
286
+ """Link an existing GLPI document to one ticket timeline.
287
+
288
+ Parameters
289
+ ----------
290
+ ticket_id : GlpiId
291
+ Numeric identifier of the parent ticket.
292
+ document_link : PostTimelineDocument
293
+ Request body describing the link (typically the timeline
294
+ position; document and ticket identifiers are inferred from
295
+ the URL).
296
+
297
+ Returns
298
+ -------
299
+ int
300
+ Identifier assigned by the GLPI server to the new link.
301
+
302
+ Raises
303
+ ------
304
+ GlpiStatusError
305
+ If the GLPI server returns a non-success HTTP status.
306
+ GlpiProtocolError
307
+ If the create response is missing the ``id`` field.
308
+ """
309
+
310
+ return await self._resource_create(
311
+ f"{TICKET_ENDPOINT}/{ticket_id}/{TIMELINE_DOCUMENT_SUFFIX}",
312
+ document_link,
313
+ failure_message=(f"Failed to link timeline document on ticket {ticket_id}"),
314
+ missing_message=(
315
+ "GLPI timeline document link response did not include an ID"
316
+ ),
317
+ log_message_factory=(
318
+ lambda new_id: (
319
+ f"GLPI API linked timeline document {new_id} on ticket {ticket_id}"
320
+ )
321
+ ),
322
+ )
323
+
324
+ async def update_ticket_timeline_document(
325
+ self,
326
+ ticket_id: GlpiId,
327
+ document_link_id: GlpiId,
328
+ document_link: PatchTimelineDocument,
329
+ ) -> None:
330
+ """Update one timeline document link with a partial body.
331
+
332
+ Parameters
333
+ ----------
334
+ ticket_id : GlpiId
335
+ Numeric identifier of the parent ticket.
336
+ document_link_id : GlpiId
337
+ Numeric identifier of the timeline document link to update.
338
+ document_link : PatchTimelineDocument
339
+ Partial request body.
340
+
341
+ Returns
342
+ -------
343
+ None
344
+
345
+ Raises
346
+ ------
347
+ GlpiStatusError
348
+ If the GLPI server returns a non-success HTTP status.
349
+ """
350
+
351
+ await self._resource_update(
352
+ f"{TICKET_ENDPOINT}/{ticket_id}/"
353
+ f"{TIMELINE_DOCUMENT_SUFFIX}/{document_link_id}",
354
+ document_link,
355
+ failure_message=(
356
+ f"Failed to update timeline document {document_link_id} on "
357
+ f"ticket {ticket_id}"
358
+ ),
359
+ log_message=(
360
+ f"GLPI API updated timeline document {document_link_id} on "
361
+ f"ticket {ticket_id}"
362
+ ),
363
+ )
364
+
365
+ async def unlink_ticket_timeline_document(
366
+ self,
367
+ ticket_id: GlpiId,
368
+ document_link_id: GlpiId,
369
+ *,
370
+ force: bool | None = None,
371
+ ) -> None:
372
+ """Unlink one timeline document from a ticket.
373
+
374
+ Parameters
375
+ ----------
376
+ ticket_id : GlpiId
377
+ Numeric identifier of the parent ticket.
378
+ document_link_id : GlpiId
379
+ Numeric identifier of the timeline document link to remove.
380
+ force : bool | None, optional
381
+ When ``True`` the link is permanently deleted instead of
382
+ being moved to the trash.
383
+
384
+ Returns
385
+ -------
386
+ None
387
+
388
+ Raises
389
+ ------
390
+ GlpiStatusError
391
+ If the GLPI server returns a non-success HTTP status.
392
+ """
393
+
394
+ await self._resource_delete(
395
+ f"{TICKET_ENDPOINT}/{ticket_id}/"
396
+ f"{TIMELINE_DOCUMENT_SUFFIX}/{document_link_id}",
397
+ failure_message=(
398
+ f"Failed to unlink timeline document {document_link_id} on "
399
+ f"ticket {ticket_id}"
400
+ ),
401
+ log_message=(
402
+ f"GLPI API unlinked timeline document {document_link_id} on "
403
+ f"ticket {ticket_id}"
404
+ ),
405
+ force=force,
406
+ delete_model_cls=DeleteTimelineDocument,
407
+ )
408
+
409
+
410
+ __all__ = ["TimelineDocumentMixin"]