glpi-python-client 0.6.2__tar.gz → 0.6.4__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.
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/CHANGELOG.md +56 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/PKG-INFO +1 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/api_reference.rst +10 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/user_guide.rst +96 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/__init__.py +5 -1
- glpi_python_client-0.6.4/glpi_python_client/_async/clients/api/assistance/timeline/_document.py +410 -0
- glpi_python_client-0.6.4/glpi_python_client/_sync/clients/api/assistance/timeline/_document.py +410 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/content/__init__.py +6 -2
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/content/conversion.py +277 -21
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/__init__.py +4 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/enums.py +5 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/management/__init__.py +6 -0
- glpi_python_client-0.6.4/glpi_python_client/models/api_schema/management/_document_item.py +88 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/pyproject.toml +1 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-asset-workflow/SKILL.md +1 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-client-setup/SKILL.md +1 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-contract-workflow/SKILL.md +1 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-document-workflow/SKILL.md +3 -2
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-knowledge-base/SKILL.md +1 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-plugin-fields/SKILL.md +1 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-reporting-and-context/SKILL.md +1 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-team-members/SKILL.md +1 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-ticket-timeline/SKILL.md +2 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-ticket-workflow/SKILL.md +1 -1
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/glpi-user-location-provisioning/SKILL.md +1 -1
- glpi_python_client-0.6.2/glpi_python_client/_async/clients/api/assistance/timeline/_document.py +0 -225
- glpi_python_client-0.6.2/glpi_python_client/_sync/clients/api/assistance/timeline/_document.py +0 -225
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/.gitignore +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/.pre-commit-config.yaml +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/.readthedocs.yaml +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/CONTRIBUTING.md +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/LICENSE +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/README.md +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/_static/.gitkeep +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/conf.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/development.md +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/development_rtd.rst +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/index.rst +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/installation.rst +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/publishing.md +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/publishing_rtd.rst +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/docs/sponsoring.rst +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/_concurrency.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/_testing.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/auth/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/auth/_v1_session.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/auth/auth.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/_base_client.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/administration/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/administration/_entity.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/administration/_user.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/assets/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/assets/_computer.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/assistance/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/assistance/_team.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/assistance/_ticket.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/assistance/timeline/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/assistance/timeline/_followup.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/assistance/timeline/_solution.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/assistance/timeline/_task.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/dropdowns/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/dropdowns/_contract_type.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/dropdowns/_location.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/knowledgebase/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/knowledgebase/_article.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/knowledgebase/_category.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/knowledgebase/_comment.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/knowledgebase/_revision.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/management/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/management/_contract.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/management/_document.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/plugins/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/api/plugins/_fields.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/client.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/commons/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/commons/_config.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/commons/_constants.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/commons/_filters.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/commons/_http.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/commons/_payloads.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/commons/_transport.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/custom/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/custom/_statistics.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_async/clients/custom/_ticket_context.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_errors.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/_concurrency.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/_testing.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/auth/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/auth/_v1_session.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/auth/auth.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/_base_client.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/administration/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/administration/_entity.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/administration/_user.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/assets/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/assets/_computer.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/assistance/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/assistance/_team.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/assistance/_ticket.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/assistance/timeline/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/assistance/timeline/_followup.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/assistance/timeline/_solution.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/assistance/timeline/_task.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/dropdowns/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/dropdowns/_contract_type.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/dropdowns/_location.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/knowledgebase/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/knowledgebase/_article.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/knowledgebase/_category.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/knowledgebase/_comment.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/knowledgebase/_revision.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/management/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/management/_contract.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/management/_document.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/plugins/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/api/plugins/_fields.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/client.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/commons/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/commons/_config.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/commons/_constants.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/commons/_filters.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/commons/_http.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/commons/_payloads.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/commons/_transport.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/custom/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/custom/_statistics.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/_sync/clients/custom/_ticket_context.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/_base.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/_common.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/_content.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/administration/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/administration/_entity.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/administration/_user.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assets/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assets/_computer.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assets/_contract_item.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assistance/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assistance/_team.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assistance/_ticket.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assistance/timeline/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assistance/timeline/_document.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assistance/timeline/_followup.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assistance/timeline/_solution.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/assistance/timeline/_task.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/dropdowns/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/dropdowns/_contract_type.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/dropdowns/_location.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/knowledgebase/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/knowledgebase/_article.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/knowledgebase/_category.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/knowledgebase/_comment.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/knowledgebase/_revision.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/management/_contract.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/management/_contract_cost.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/management/_document.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/plugins/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/api_schema/plugins/_fields.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/custom_schema/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/models/custom_schema/_ticket_context.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/py.typed +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/rsql.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/testing/__init__.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/testing/fixtures.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/glpi_python_client/testing/utils.py +0 -0
- {glpi_python_client-0.6.2 → glpi_python_client-0.6.4}/skills/README.md +0 -0
|
@@ -4,6 +4,62 @@ 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.4 — 2026-10-12
|
|
8
|
+
|
|
9
|
+
A patch release that reads the documents GLPI links to a followup. GLPI's
|
|
10
|
+
editor links an image pasted into a followup to the followup and not to the
|
|
11
|
+
ticket, so the ticket-level listing never returned it. Existing methods are
|
|
12
|
+
unchanged.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`list_item_documents(itemtype, items_id)`** lists the v1 `Document_Item`
|
|
17
|
+
links of one item as `GetDocumentItem` rows. It needs the v1 session
|
|
18
|
+
(`v1_base_url` and `v1_user_token`) and raises `RuntimeError` without one.
|
|
19
|
+
An invalid `itemtype` or id, a non-list answer, or a full page of 1000 rows
|
|
20
|
+
or more raises `GlpiValidationError`; a partial list is never returned.
|
|
21
|
+
- **`GetDocumentItem`**, exported from the package root, models a link row:
|
|
22
|
+
`id`, `documents_id`, `itemtype`, `items_id`, `entities_id`, the dates and
|
|
23
|
+
`timeline_position`. Other keys stay in `extra_payload`.
|
|
24
|
+
- **`list_ticket_followup_documents(ticket_id, *, include_private=False)`**
|
|
25
|
+
returns one `FollowupDocuments` (the followup and its `GetDocumentItem`
|
|
26
|
+
links) per followup, in ascending followup id, empty ones included. Private
|
|
27
|
+
followups, and followups with no `is_private` flag, are dropped by default;
|
|
28
|
+
`include_private=True` keeps every followup.
|
|
29
|
+
- **`FollowupDocuments`**, exported from the package root.
|
|
30
|
+
- **`GlpiTimelinePosition.NO_TIMELINE`** (-1) is an alias of `INVALID`, so
|
|
31
|
+
existing uses of `INVALID` are unaffected.
|
|
32
|
+
- The followup route was measured live. The primitive `list_item_documents`
|
|
33
|
+
does not filter private followups; the convenience filters them by default.
|
|
34
|
+
Tasks and solutions are not measured, and no helper walks them.
|
|
35
|
+
|
|
36
|
+
## 0.6.3 — 2026-10-08
|
|
37
|
+
|
|
38
|
+
A patch release of the converter's reading: a caller can recognise the
|
|
39
|
+
documents a body embeds or links and decide, link by link, what a read
|
|
40
|
+
writes. Without the new argument a read is unchanged, byte for byte; writing
|
|
41
|
+
is unchanged. `easyvista-python-client` 0.4.3 adds the same hook.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **`from_transport(..., rewrite_link=callback)`.** The callback is called with
|
|
46
|
+
a `Link` for each link and image the read meets outside code -- an image
|
|
47
|
+
before the link around it, with that link's `enclosing_href` -- and its
|
|
48
|
+
answer is written: `None` keeps the reader's output, a `str` is written as
|
|
49
|
+
literal text, a `Link` replaces the target (an empty `href` drops a link and
|
|
50
|
+
keeps its content, or writes an image as its text). The callback is held in
|
|
51
|
+
a context variable, so concurrent reads each use their own. What it raises
|
|
52
|
+
reaches the caller unchanged; a `ValueError` is not taken for markdownify's
|
|
53
|
+
and answered with the text fallback.
|
|
54
|
+
- **`Link` and `RewriteLink`**, exported from `glpi_python_client.content`.
|
|
55
|
+
- **`GlpiContentConverter.document_id_of(url)`** returns the document id a URL
|
|
56
|
+
to `front/document.send.php` names (relative, rooted or absolute, one
|
|
57
|
+
`docid` in any position), and **`document_image(document_id, alt=...,
|
|
58
|
+
itemtype=..., items_id=...)`** the Markdown of an embedded document image as
|
|
59
|
+
GLPI's editor writes one -- the image inside a link to the same URL --
|
|
60
|
+
spelled as the reader spells it. A `Link` whose URL names a document carries
|
|
61
|
+
its `document_id`.
|
|
62
|
+
|
|
7
63
|
## 0.6.2 — 2026-10-07
|
|
8
64
|
|
|
9
65
|
A patch release of the converter's writing: a link written into GLPI opens in
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: glpi-python-client
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
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
|
|
|
@@ -1912,6 +1950,63 @@ text.
|
|
|
1912
1950
|
Prefer a patched interpreter anyway: the guard covers the converter's input,
|
|
1913
1951
|
and the CPython fix covers the parser itself.
|
|
1914
1952
|
|
|
1953
|
+
Document images, and rewriting links as they are read
|
|
1954
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
1955
|
+
|
|
1956
|
+
GLPI's editor keeps an image pasted into a body as a document and embeds it
|
|
1957
|
+
from the page that serves documents, inside a link to the same URL:
|
|
1958
|
+
``<a href="/front/document.send.php?docid=N&itemtype=Ticket&items_id=T">``
|
|
1959
|
+
around ``<img src="...same URL...">``. Without a callback the reader writes
|
|
1960
|
+
that as any linked image, ``[](url)``, which displays nowhere but in
|
|
1961
|
+
GLPI. Three helpers let a caller do better.
|
|
1962
|
+
|
|
1963
|
+
``GlpiContentConverter.document_id_of(url)`` returns the document id a URL
|
|
1964
|
+
names -- ``front/document.send.php`` with one ``docid``, relative, rooted or
|
|
1965
|
+
absolute, under any path -- or ``None``. ``document_image(document_id,
|
|
1966
|
+
alt=..., itemtype=..., items_id=...)`` returns the Markdown of the editor's
|
|
1967
|
+
form, spelled exactly as the reader spells it, so ``to_transport`` writes it
|
|
1968
|
+
and ``from_transport`` reads it back unchanged. Its URL is rooted, as the
|
|
1969
|
+
editor wrote it on the instance measured.
|
|
1970
|
+
|
|
1971
|
+
``from_transport(..., rewrite_link=callback)`` calls ``callback`` with a
|
|
1972
|
+
:class:`~glpi_python_client.content.Link` for each link and each image it
|
|
1973
|
+
meets outside code, an image before the link around it. A ``Link`` carries the
|
|
1974
|
+
``href`` (an image's ``src``), the ``text`` (an image's ``alt``), the
|
|
1975
|
+
``title``, whether it is an ``image``, the ``document_id`` its URL names, and
|
|
1976
|
+
for an image inside a link that link's ``enclosing_href``. What the callback
|
|
1977
|
+
answers is written:
|
|
1978
|
+
|
|
1979
|
+
* ``None``: what the reader writes without a callback;
|
|
1980
|
+
* a ``str``: that text, literally -- escaped as any text the body displays, so
|
|
1981
|
+
it cannot become markup;
|
|
1982
|
+
* a ``Link``: a link's ``href`` and ``title`` around the link's content as
|
|
1983
|
+
read, or an image's ``href`` as its ``src``, ``text`` as its ``alt`` and
|
|
1984
|
+
``title``;
|
|
1985
|
+
* a ``Link`` with an empty ``href``: a link is dropped and its content kept, an
|
|
1986
|
+
image is written as its ``text``.
|
|
1987
|
+
|
|
1988
|
+
.. code-block:: python
|
|
1989
|
+
|
|
1990
|
+
from glpi_python_client.content import GlpiContentConverter, Link
|
|
1991
|
+
|
|
1992
|
+
def name_documents(link: Link) -> Link | str | None:
|
|
1993
|
+
if link.document_id is None:
|
|
1994
|
+
return None
|
|
1995
|
+
if link.image:
|
|
1996
|
+
return f"[document {link.document_id}]"
|
|
1997
|
+
return Link("") # the editor's link around it: keep only its content
|
|
1998
|
+
|
|
1999
|
+
GlpiContentConverter.from_transport(body, rewrite_link=name_documents)
|
|
2000
|
+
|
|
2001
|
+
The callback is held for the duration of one read, per thread and per task,
|
|
2002
|
+
so concurrent reads each use their own, and a read inside a callback has its
|
|
2003
|
+
own too. What the callback raises reaches the caller unchanged -- a
|
|
2004
|
+
``ValueError`` included, which the reader would otherwise take for one of
|
|
2005
|
+
markdownify's and answer by reading the body as its text. An answer of any
|
|
2006
|
+
other type raises ``TypeError``. It is not called for a value passed through
|
|
2007
|
+
as Markdown (``plain_text_is_markdown=True``), nor for a body read as its text.
|
|
2008
|
+
``easyvista-python-client`` 0.4.3 carries the same hook.
|
|
2009
|
+
|
|
1915
2010
|
It is not a sanitiser
|
|
1916
2011
|
^^^^^^^^^^^^^^^^^^^^^
|
|
1917
2012
|
|
|
@@ -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.
|
|
136
|
+
__version__ = "0.6.4"
|
|
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",
|
glpi_python_client-0.6.4/glpi_python_client/_async/clients/api/assistance/timeline/_document.py
ADDED
|
@@ -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"]
|