glpi-python-client 0.3.1__tar.gz → 0.3.3__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.3.1 → glpi_python_client-0.3.3}/.gitignore +3 -2
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/PKG-INFO +1 -1
- glpi_python_client-0.3.3/docs/development_rtd.rst +162 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/user_guide.rst +49 -13
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/__init__.py +1 -1
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/assistance/timeline/_document.py +21 -20
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/async_client.py +3 -1
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/_async_bridge.py +17 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/custom/__init__.py +2 -0
- glpi_python_client-0.3.3/glpi_python_client/clients/custom/_pagination_async.py +164 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/custom/_statistics.py +21 -16
- glpi_python_client-0.3.3/glpi_python_client/clients/custom/_statistics_async.py +500 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/custom/tests/test_statistics.py +2 -2
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/tests/test_api_coverage.py +2 -2
- glpi_python_client-0.3.3/glpi_python_client/clients/tests/test_async_branches.py +701 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/custom_schema/_ticket_context.py +12 -9
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/custom_schema/tests/test_ticket_context.py +7 -7
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/pyproject.toml +1 -1
- glpi_python_client-0.3.1/docs/development_rtd.rst +0 -84
- glpi_python_client-0.3.1/glpi_python_client/clients/custom/_statistics_async.py +0 -250
- glpi_python_client-0.3.1/glpi_python_client/clients/tests/test_async_branches.py +0 -195
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/.pre-commit-config.yaml +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/.readthedocs.yaml +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/CONTRIBUTING.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/LICENSE +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/README.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/_static/.gitkeep +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/api_reference.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/conf.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/development.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/glpi_api_contract.json +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/index.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/installation.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/publishing.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/publishing_rtd.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/docs/sponsoring.rst +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/auth/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/auth/_v1_session.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/auth/auth.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/auth/tests/test_auth.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/auth/tests/test_v1_session.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/administration/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/administration/_entity.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/administration/_user.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/assistance/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/assistance/_team.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/assistance/_ticket.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/assistance/timeline/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/assistance/timeline/_followup.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/assistance/timeline/_solution.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/assistance/timeline/_task.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/dropdowns/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/dropdowns/_location.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/management/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/api/management/_document.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/_config.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/_constants.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/_errors.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/_filters.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/_http.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/_payloads.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/_transport.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/tests/test_errors.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/tests/test_filters.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/tests/test_http.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/tests/test_payloads.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/commons/tests/test_transport.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/custom/_ticket_context.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/custom/_ticket_context_async.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/custom/tests/test_statistics_async.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/custom/tests/test_ticket_context.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/sync_client.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/tests/test_async_smoke.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/tests/test_glpi_client.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/tests/test_parity.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/tests/test_smoke.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/content/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/content/conversion.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/content/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/content/tests/test_conversion.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/_base.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/_common.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/_content.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/administration/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/administration/_entity.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/administration/_user.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/administration/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/administration/tests/test_administration_schemas.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/_team.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/_ticket.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/tests/test_assistance_schemas.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/tests/test_content_roundtrip.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/timeline/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/timeline/_document.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/timeline/_followup.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/timeline/_solution.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/timeline/_task.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/timeline/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/assistance/timeline/tests/test_timeline_schemas.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/dropdowns/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/dropdowns/_location.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/dropdowns/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/dropdowns/tests/test_dropdowns_schemas.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/enums.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/management/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/management/_document.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/management/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/api_schema/management/tests/test_management_schemas.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/custom_schema/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/models/custom_schema/tests/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/py.typed +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/testing/__init__.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/testing/fixtures.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/testing/utils.py +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/skills/README.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/skills/glpi-client-setup/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/skills/glpi-document-workflow/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/skills/glpi-reporting-and-context/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/skills/glpi-team-members/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/skills/glpi-ticket-timeline/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/skills/glpi-ticket-workflow/SKILL.md +0 -0
- {glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/skills/glpi-user-location-provisioning/SKILL.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: glpi-python-client
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
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/
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
Development
|
|
2
|
+
===========
|
|
3
|
+
|
|
4
|
+
Local Setup
|
|
5
|
+
-----------
|
|
6
|
+
|
|
7
|
+
Create a virtual environment and install the development dependencies:
|
|
8
|
+
|
|
9
|
+
.. code-block:: console
|
|
10
|
+
|
|
11
|
+
python -m venv .venv
|
|
12
|
+
.venv\Scripts\activate
|
|
13
|
+
python -m pip install --upgrade pip
|
|
14
|
+
python -m pip install -e .[dev]
|
|
15
|
+
python -m pre_commit install
|
|
16
|
+
|
|
17
|
+
The repository ships a root ``.pre-commit-config.yaml`` that runs Ruff on each
|
|
18
|
+
commit. The lint hook applies safe fixes first, then Ruff formats the touched
|
|
19
|
+
files.
|
|
20
|
+
|
|
21
|
+
Quality Checks
|
|
22
|
+
--------------
|
|
23
|
+
|
|
24
|
+
Run the focused checks before opening a pull request:
|
|
25
|
+
|
|
26
|
+
.. code-block:: console
|
|
27
|
+
|
|
28
|
+
python -m pre_commit run --all-files
|
|
29
|
+
python -m pytest
|
|
30
|
+
python -m ruff check .
|
|
31
|
+
python -m mypy glpi_python_client
|
|
32
|
+
python -m sphinx -b html docs docs/_build/html
|
|
33
|
+
|
|
34
|
+
Integration Tests
|
|
35
|
+
-----------------
|
|
36
|
+
|
|
37
|
+
The ``integration_tests/`` directory holds end-to-end tests that drive a live
|
|
38
|
+
GLPI instance through the synchronous and asynchronous clients. They are
|
|
39
|
+
collected only when secrets resolve to a reachable instance; otherwise each
|
|
40
|
+
test self-skips via ``pytest.skip``. Every test cleans up the records it
|
|
41
|
+
creates in a ``finally`` block, but it is still recommended to point them at
|
|
42
|
+
a non-production GLPI.
|
|
43
|
+
|
|
44
|
+
Markers and CI behaviour
|
|
45
|
+
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
46
|
+
|
|
47
|
+
All integration tests are tagged with the ``integration`` pytest marker
|
|
48
|
+
declared in ``pyproject.toml``. The ``ci.yml`` workflow runs
|
|
49
|
+
``pytest -m "not integration"`` for both the test matrix and the coverage
|
|
50
|
+
job, so the public CI never reaches a live GLPI. The default local
|
|
51
|
+
``python -m pytest`` invocation *does* attempt to collect them, but they
|
|
52
|
+
skip automatically when no secrets are configured.
|
|
53
|
+
|
|
54
|
+
To explicitly opt in or out locally:
|
|
55
|
+
|
|
56
|
+
.. code-block:: console
|
|
57
|
+
|
|
58
|
+
python -m pytest -m integration # run only the live suite
|
|
59
|
+
python -m pytest -m "not integration" # mirror the CI behaviour
|
|
60
|
+
|
|
61
|
+
Configuration
|
|
62
|
+
~~~~~~~~~~~~~
|
|
63
|
+
|
|
64
|
+
The suite reads each value from a file named after the secret under
|
|
65
|
+
``secrets/`` at the repository root, falling back to the matching
|
|
66
|
+
environment variable when the file is absent. The ``secrets/`` directory is
|
|
67
|
+
gitignored. Each file contains a single trimmed value.
|
|
68
|
+
|
|
69
|
+
Required:
|
|
70
|
+
|
|
71
|
+
============================== ========================= ===========================================
|
|
72
|
+
Secret file Environment variable Purpose
|
|
73
|
+
============================== ========================= ===========================================
|
|
74
|
+
``glpi_api_url`` ``GLPI_API_URL`` Base URL of the GLPI v2 API.
|
|
75
|
+
``glpi_client_id_test`` ``GLPI_CLIENT_ID`` OAuth2 client identifier.
|
|
76
|
+
``glpi_client_secret_test`` ``GLPI_CLIENT_SECRET`` OAuth2 client secret.
|
|
77
|
+
``glpi_username`` ``GLPI_USERNAME`` GLPI user for the password grant.
|
|
78
|
+
``glpi_password`` ``GLPI_PASSWORD`` Password for the GLPI user above.
|
|
79
|
+
============================== ========================= ===========================================
|
|
80
|
+
|
|
81
|
+
Optional:
|
|
82
|
+
|
|
83
|
+
============================== ============================= ============================================
|
|
84
|
+
Secret file Environment variable Purpose
|
|
85
|
+
============================== ============================= ============================================
|
|
86
|
+
``glpi_verify_ssl`` ``GLPI_VERIFY_SSL`` Toggle TLS verification (default ``false``).
|
|
87
|
+
``glpi_entity`` ``GLPI_ENTITY`` Active entity id sent on every request.
|
|
88
|
+
``glpi_profile`` ``GLPI_PROFILE`` Active profile id sent on every request.
|
|
89
|
+
``glpi_entity_recursive`` ``GLPI_ENTITY_RECURSIVE`` Include sub-entities (default ``false``).
|
|
90
|
+
``glpi_api_v1_url`` ``GLPI_API_V1_URL`` Base URL of the legacy v1 API.
|
|
91
|
+
``glpi_api_v1_token_user`` ``GLPI_V1_USER_TOKEN`` v1 user token (enables document uploads).
|
|
92
|
+
``glpi_api_v1_app_token`` ``GLPI_V1_APP_TOKEN`` v1 application token paired with the above.
|
|
93
|
+
``glpi_team_member_role`` ``GLPI_TEAM_MEMBER_ROLE`` Role used to add the test user to a ticket.
|
|
94
|
+
============================== ============================= ============================================
|
|
95
|
+
|
|
96
|
+
The v1 secrets are only required for the document-upload test; when they
|
|
97
|
+
are missing that single test skips while the rest of the suite runs.
|
|
98
|
+
|
|
99
|
+
Running the suite
|
|
100
|
+
~~~~~~~~~~~~~~~~~
|
|
101
|
+
|
|
102
|
+
Once secrets are in place:
|
|
103
|
+
|
|
104
|
+
.. code-block:: console
|
|
105
|
+
|
|
106
|
+
python -m pytest integration_tests -m integration
|
|
107
|
+
|
|
108
|
+
Use a disposable GLPI instance or one whose entity is dedicated to
|
|
109
|
+
automated tests. The suite creates and deletes users, locations, tickets,
|
|
110
|
+
followups, tasks, and solutions on every run.
|
|
111
|
+
|
|
112
|
+
Package Layout
|
|
113
|
+
--------------
|
|
114
|
+
|
|
115
|
+
``glpi_python_client.__init__``
|
|
116
|
+
Public import surface (``GlpiClient``, ``GlpiTicketContext``, public Pydantic
|
|
117
|
+
models, enums, and ``__version__``).
|
|
118
|
+
|
|
119
|
+
``glpi_python_client.clients.glpi_client``
|
|
120
|
+
Composition root. ``GlpiClient`` mixes the per-resource async API mixins,
|
|
121
|
+
the OAuth2 token manager, the asynchronous v2 transport, and the optional
|
|
122
|
+
internal v1 session used for document uploads.
|
|
123
|
+
|
|
124
|
+
``glpi_python_client.clients.api``
|
|
125
|
+
Async API mixins generated from the GLPI v2 OpenAPI contract: tickets,
|
|
126
|
+
ticket timeline (followups, tasks, solutions, documents), team members,
|
|
127
|
+
documents, users, locations, entities, ...
|
|
128
|
+
|
|
129
|
+
``glpi_python_client.clients.custom``
|
|
130
|
+
Higher-level helpers built on top of the contract mixins:
|
|
131
|
+
``get_ticket_context``, ``get_ticket_statistics``, ``get_task_statistics``.
|
|
132
|
+
|
|
133
|
+
``glpi_python_client.clients.commons``
|
|
134
|
+
Shared HTTP transport pieces, including the timeline envelope unwrap that
|
|
135
|
+
reconciles live server behaviour with the OpenAPI contract.
|
|
136
|
+
|
|
137
|
+
``glpi_python_client.models.api_schema``
|
|
138
|
+
Contract-aligned Pydantic v2 models (``Get``/``Post``/``Patch``/``Delete``)
|
|
139
|
+
for each GLPI v2 resource.
|
|
140
|
+
|
|
141
|
+
``glpi_python_client.models.custom_schema``
|
|
142
|
+
Composite models such as ``GlpiTicketContext`` returned by the custom
|
|
143
|
+
helpers.
|
|
144
|
+
|
|
145
|
+
Adding Endpoints
|
|
146
|
+
----------------
|
|
147
|
+
|
|
148
|
+
#. Add or extend the contract-aligned models in
|
|
149
|
+
``glpi_python_client.models.api_schema``.
|
|
150
|
+
#. Add the async mixin and method under ``glpi_python_client.clients.api``,
|
|
151
|
+
mirroring the OpenAPI path and HTTP verb.
|
|
152
|
+
#. When the live server diverges from the contract, document the choice in the
|
|
153
|
+
module docstring and (when needed) wire an unwrap helper from
|
|
154
|
+
``glpi_python_client.clients.commons``.
|
|
155
|
+
#. Re-export new public symbols from ``glpi_python_client.__init__``.
|
|
156
|
+
#. Add tests for payload serialization, response parsing, and client behaviour.
|
|
157
|
+
#. Document the workflow in :doc:`user_guide` and the matching skill in
|
|
158
|
+
``skills/``.
|
|
159
|
+
|
|
160
|
+
Keep organization-specific entity, profile, and category defaults outside the
|
|
161
|
+
library core. Applications can apply their own mapping before calling the
|
|
162
|
+
client.
|
|
@@ -179,16 +179,40 @@ fan-outs cannot race the auth manager, while the HTTP requests
|
|
|
179
179
|
themselves execute outside the lock through the thread-safe
|
|
180
180
|
:class:`requests.Session`.
|
|
181
181
|
|
|
182
|
-
A
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
182
|
+
A number of helpers ship with hand-written async overrides rather than
|
|
183
|
+
relying solely on the bridge. There are two reasons a method needs its
|
|
184
|
+
own async variant:
|
|
185
|
+
|
|
186
|
+
1. **Concurrency** — the method benefits from fanning multiple GLPI
|
|
187
|
+
calls out concurrently with :func:`asyncio.gather`.
|
|
188
|
+
2. **Internal self-calls** — the method calls another public method
|
|
189
|
+
through ``self`` (e.g. ``self.search_tickets(...)`` inside a
|
|
190
|
+
pagination loop). When the bridge runs the synchronous body in a
|
|
191
|
+
worker thread, ``self.method`` resolves to the bridge-wrapped
|
|
192
|
+
*coroutine*, which returns a coroutine object instead of data when
|
|
193
|
+
called without ``await``. The async override replaces the body so
|
|
194
|
+
every internal call is properly awaited on the event loop.
|
|
195
|
+
|
|
196
|
+
Helpers with async overrides:
|
|
197
|
+
|
|
198
|
+
* :meth:`AsyncGlpiClient.get_ticket_context` — fans the five underlying
|
|
199
|
+
GLPI calls out concurrently (reason: concurrency).
|
|
200
|
+
* :meth:`AsyncGlpiClient.get_task_statistics` — fans the per-ticket
|
|
201
|
+
task-list calls out concurrently (reason: concurrency).
|
|
202
|
+
* :meth:`AsyncGlpiClient.get_task_durations` — fans the per-ticket task
|
|
203
|
+
fetches out concurrently when ``return_task_details=True``, and
|
|
204
|
+
properly awaits ``iter_search_tickets`` and ``search_entities``
|
|
205
|
+
internally (reasons: concurrency + internal self-calls).
|
|
206
|
+
* :meth:`AsyncGlpiClient.get_ticket_statistics` — properly awaits
|
|
207
|
+
``search_tickets`` and ``search_entities`` internally (reason:
|
|
208
|
+
internal self-calls).
|
|
209
|
+
* :meth:`AsyncGlpiClient.get_user_activity` — properly awaits
|
|
210
|
+
``search_users``, ``iter_search_tickets``, and ``get_task_durations``
|
|
211
|
+
internally (reason: internal self-calls).
|
|
212
|
+
* ``iter_search_tickets``, ``iter_search_users``,
|
|
213
|
+
``iter_search_entities`` — each pagination loop body calls
|
|
214
|
+
``self.search_*(...)``; the async variants are native async generators
|
|
215
|
+
that ``await`` those calls directly (reason: internal self-calls).
|
|
192
216
|
|
|
193
217
|
Pagination helpers (``iter_search_tickets``, ``iter_search_users``,
|
|
194
218
|
``iter_search_entities``) are exposed as **async generators** on the
|
|
@@ -752,6 +776,14 @@ batches until the API returns fewer rows than the requested
|
|
|
752
776
|
print(ticket.id, ticket.name)
|
|
753
777
|
print(f"processed {total} tickets")
|
|
754
778
|
|
|
779
|
+
.. note::
|
|
780
|
+
|
|
781
|
+
Always pass an RSQL filter to ``iter_search_tickets``. Querying
|
|
782
|
+
without any filter can return very large result sets and may cause
|
|
783
|
+
the GLPI server to return a 500 error on busy instances. The other
|
|
784
|
+
two generators (``iter_search_users``, ``iter_search_entities``) are
|
|
785
|
+
not affected because those collections are typically much smaller.
|
|
786
|
+
|
|
755
787
|
On the asynchronous client the same helpers are exposed as **async
|
|
756
788
|
generators** through the bridge, so each ``next()`` call runs off the
|
|
757
789
|
event loop and the consumer uses ``async for``:
|
|
@@ -766,8 +798,10 @@ event loop and the consumer uses ``async for``:
|
|
|
766
798
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
767
799
|
|
|
768
800
|
Counts tickets created within an ISO date window and groups them by
|
|
769
|
-
entity, status, priority, and type.
|
|
770
|
-
|
|
801
|
+
entity, status, priority, and type. The ``start_date`` is inclusive
|
|
802
|
+
from 00:00:00 and the ``end_date`` is inclusive through 23:59:59, so
|
|
803
|
+
tickets created at any time on those days are counted. Optional
|
|
804
|
+
filters restrict the result set on the server side:
|
|
771
805
|
|
|
772
806
|
* ``entity_id`` — restrict to a single entity by numeric identifier.
|
|
773
807
|
* ``entity_name`` — substring match against the entity ``name`` column;
|
|
@@ -869,7 +903,9 @@ then computes per-user and per-entity totals.
|
|
|
869
903
|
Available filters:
|
|
870
904
|
|
|
871
905
|
* ``start_date`` / ``end_date`` / ``default_days`` — ISO ``YYYY-MM-DD``
|
|
872
|
-
date window; ``
|
|
906
|
+
date window; ``start_date`` is inclusive from 00:00:00,
|
|
907
|
+
``end_date`` is inclusive through 23:59:59, and ``default_days``
|
|
908
|
+
is used when ``start_date`` is omitted.
|
|
873
909
|
* ``entity_id`` — restrict to a single entity by identifier.
|
|
874
910
|
* ``entity_name`` — substring match resolved through ``search_entities``;
|
|
875
911
|
ignored when ``entity_id`` is given.
|
|
@@ -7,11 +7,12 @@ Notes
|
|
|
7
7
|
-----
|
|
8
8
|
The live GLPI v2 server returns each entry of the list endpoint wrapped
|
|
9
9
|
in a ``{"type": "Document_Item", "item": {...}}`` envelope, even though
|
|
10
|
-
the OpenAPI contract documents a flat array of ``Document_Item``.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
:
|
|
14
|
-
helper and
|
|
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`.
|
|
15
16
|
"""
|
|
16
17
|
|
|
17
18
|
from __future__ import annotations
|
|
@@ -24,19 +25,17 @@ from glpi_python_client.clients.commons._constants import (
|
|
|
24
25
|
from glpi_python_client.clients.commons._transport import TransportMixin
|
|
25
26
|
from glpi_python_client.models.api_schema.assistance.timeline._document import (
|
|
26
27
|
DeleteTimelineDocument,
|
|
27
|
-
GetTimelineDocument,
|
|
28
28
|
PatchTimelineDocument,
|
|
29
29
|
PostTimelineDocument,
|
|
30
30
|
)
|
|
31
|
+
from glpi_python_client.models.api_schema.management._document import GetDocument
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
class TimelineDocumentMixin(TransportMixin):
|
|
34
35
|
"""Synchronous CRUD helpers for the ticket document timeline endpoint."""
|
|
35
36
|
|
|
36
|
-
def list_ticket_timeline_documents(
|
|
37
|
-
|
|
38
|
-
) -> list[GetTimelineDocument]:
|
|
39
|
-
"""List all timeline documents linked to one ticket.
|
|
37
|
+
def list_ticket_timeline_documents(self, ticket_id: GlpiId) -> list[GetDocument]:
|
|
38
|
+
"""List all documents linked to one ticket timeline.
|
|
40
39
|
|
|
41
40
|
Parameters
|
|
42
41
|
----------
|
|
@@ -45,14 +44,16 @@ class TimelineDocumentMixin(TransportMixin):
|
|
|
45
44
|
|
|
46
45
|
Returns
|
|
47
46
|
-------
|
|
48
|
-
list[
|
|
49
|
-
Document
|
|
50
|
-
|
|
47
|
+
list[GetDocument]
|
|
48
|
+
Document records returned by the GLPI server. The live API
|
|
49
|
+
wraps each entry in a ``{"type": "Document_Item", "item": {...}}``
|
|
50
|
+
envelope whose ``item`` value is a full ``Document`` record; the
|
|
51
|
+
envelope is unwrapped automatically.
|
|
51
52
|
"""
|
|
52
53
|
|
|
53
54
|
return self._resource_list(
|
|
54
55
|
f"{TICKET_ENDPOINT}/{ticket_id}/{TIMELINE_DOCUMENT_SUFFIX}",
|
|
55
|
-
|
|
56
|
+
GetDocument,
|
|
56
57
|
failure_message=(
|
|
57
58
|
f"Failed to list timeline documents for ticket {ticket_id}"
|
|
58
59
|
),
|
|
@@ -61,20 +62,20 @@ class TimelineDocumentMixin(TransportMixin):
|
|
|
61
62
|
|
|
62
63
|
def get_ticket_timeline_document(
|
|
63
64
|
self, ticket_id: GlpiId, document_link_id: GlpiId
|
|
64
|
-
) ->
|
|
65
|
-
"""Fetch one
|
|
65
|
+
) -> GetDocument:
|
|
66
|
+
"""Fetch one document linked to the ticket timeline by its document ID.
|
|
66
67
|
|
|
67
68
|
Parameters
|
|
68
69
|
----------
|
|
69
70
|
ticket_id : GlpiId
|
|
70
71
|
Numeric identifier of the parent ticket.
|
|
71
72
|
document_link_id : GlpiId
|
|
72
|
-
Numeric identifier of the
|
|
73
|
+
Numeric identifier of the linked document to retrieve.
|
|
73
74
|
|
|
74
75
|
Returns
|
|
75
76
|
-------
|
|
76
|
-
|
|
77
|
-
Validated document
|
|
77
|
+
GetDocument
|
|
78
|
+
Validated document payload.
|
|
78
79
|
|
|
79
80
|
Raises
|
|
80
81
|
------
|
|
@@ -85,7 +86,7 @@ class TimelineDocumentMixin(TransportMixin):
|
|
|
85
86
|
return self._resource_get(
|
|
86
87
|
f"{TICKET_ENDPOINT}/{ticket_id}/"
|
|
87
88
|
f"{TIMELINE_DOCUMENT_SUFFIX}/{document_link_id}",
|
|
88
|
-
|
|
89
|
+
GetDocument,
|
|
89
90
|
failure_message=(
|
|
90
91
|
f"Failed to get timeline document {document_link_id} on "
|
|
91
92
|
f"ticket {ticket_id}"
|
{glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/async_client.py
RENAMED
|
@@ -50,6 +50,7 @@ from glpi_python_client.clients.commons._config import (
|
|
|
50
50
|
build_client_resources,
|
|
51
51
|
)
|
|
52
52
|
from glpi_python_client.clients.commons._transport import TransportMixin
|
|
53
|
+
from glpi_python_client.clients.custom._pagination_async import AsyncPaginationMixin
|
|
53
54
|
from glpi_python_client.clients.custom._statistics_async import AsyncStatisticsMixin
|
|
54
55
|
from glpi_python_client.clients.custom._ticket_context_async import (
|
|
55
56
|
AsyncTicketContextMixin,
|
|
@@ -61,8 +62,9 @@ if TYPE_CHECKING:
|
|
|
61
62
|
logger = logging.getLogger(__name__)
|
|
62
63
|
|
|
63
64
|
|
|
64
|
-
class AsyncGlpiClient(
|
|
65
|
+
class AsyncGlpiClient( # type: ignore[misc]
|
|
65
66
|
AsyncBridge,
|
|
67
|
+
AsyncPaginationMixin,
|
|
66
68
|
TicketMixin,
|
|
67
69
|
TicketTaskMixin,
|
|
68
70
|
FollowupMixin,
|
|
@@ -22,6 +22,23 @@ Concurrency notes
|
|
|
22
22
|
releases the awaiter immediately, but the in-flight HTTP request keeps
|
|
23
23
|
running on the worker thread until ``requests`` returns. This matches
|
|
24
24
|
the behaviour of the original async client.
|
|
25
|
+
|
|
26
|
+
Known limitation — internal ``self``-calls
|
|
27
|
+
------------------------------------------
|
|
28
|
+
The bridge wraps *every* public method on the async client class. As a
|
|
29
|
+
result, when a synchronous body that is running inside a worker thread
|
|
30
|
+
calls another public method through ``self`` (e.g.
|
|
31
|
+
``self.search_tickets(...)``), it resolves to the *bridge-wrapped*
|
|
32
|
+
coroutine, not the synchronous function. Calling a coroutine without
|
|
33
|
+
``await`` produces a dangling coroutine object, not data.
|
|
34
|
+
|
|
35
|
+
Any sync method (or generator) that internally calls other public
|
|
36
|
+
methods through ``self`` must therefore be given a hand-written async
|
|
37
|
+
override that ``await``s (or ``async for``s) those calls on the event
|
|
38
|
+
loop. The convention used in this codebase is to place such overrides
|
|
39
|
+
in a ``_*_async.py`` companion module (e.g.
|
|
40
|
+
``clients/custom/_statistics_async.py``) and wire them into the async
|
|
41
|
+
client's MRO *before* the sync mixin that defines the original method.
|
|
25
42
|
"""
|
|
26
43
|
|
|
27
44
|
from __future__ import annotations
|
{glpi_python_client-0.3.1 → glpi_python_client-0.3.3}/glpi_python_client/clients/custom/__init__.py
RENAMED
|
@@ -17,6 +17,7 @@ composed into :class:`~glpi_python_client.clients.AsyncGlpiClient`.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
from glpi_python_client.clients.custom._pagination_async import AsyncPaginationMixin
|
|
20
21
|
from glpi_python_client.clients.custom._statistics import StatisticsMixin
|
|
21
22
|
from glpi_python_client.clients.custom._statistics_async import AsyncStatisticsMixin
|
|
22
23
|
from glpi_python_client.clients.custom._ticket_context import TicketContextMixin
|
|
@@ -25,6 +26,7 @@ from glpi_python_client.clients.custom._ticket_context_async import (
|
|
|
25
26
|
)
|
|
26
27
|
|
|
27
28
|
__all__ = [
|
|
29
|
+
"AsyncPaginationMixin",
|
|
28
30
|
"AsyncStatisticsMixin",
|
|
29
31
|
"AsyncTicketContextMixin",
|
|
30
32
|
"StatisticsMixin",
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Asynchronous overrides for the three paginated search generators.
|
|
2
|
+
|
|
3
|
+
Each of the three ``iter_search_*`` generators in the API mixins
|
|
4
|
+
delegates its pagination loop to a ``self.search_*()`` call. When the
|
|
5
|
+
:class:`~glpi_python_client.clients.commons._async_bridge.AsyncBridge`
|
|
6
|
+
wraps a sync generator it drives ``next()`` on the generator object inside
|
|
7
|
+
a worker thread. Inside that thread ``self`` is still the
|
|
8
|
+
:class:`~glpi_python_client.clients.AsyncGlpiClient` instance, so
|
|
9
|
+
``self.search_tickets(...)`` resolves to the bridge-wrapped coroutine
|
|
10
|
+
function and calling it without ``await`` returns a coroutine object
|
|
11
|
+
instead of the expected list — triggering a
|
|
12
|
+
``RuntimeWarning: coroutine … was never awaited`` and a 500 response.
|
|
13
|
+
|
|
14
|
+
This mixin replaces all three generators with native async generators that
|
|
15
|
+
``await self.search_*(...)`` directly on the event loop.
|
|
16
|
+
|
|
17
|
+
The mixin must be positioned **before** ``TicketMixin``, ``UserMixin``, and
|
|
18
|
+
``EntityMixin`` in the :class:`~glpi_python_client.clients.AsyncGlpiClient`
|
|
19
|
+
base list so that the bridge's ``__init_subclass__`` hook finds the async
|
|
20
|
+
generator via ``getattr`` before it would otherwise wrap the sync version.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from collections.abc import AsyncIterator
|
|
26
|
+
|
|
27
|
+
from glpi_python_client.models.api_schema.administration._entity import GetEntity
|
|
28
|
+
from glpi_python_client.models.api_schema.administration._user import GetUser
|
|
29
|
+
from glpi_python_client.models.api_schema.assistance._ticket import GetTicket
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AsyncPaginationMixin:
|
|
33
|
+
"""Async generator overrides for the three paginated search helpers.
|
|
34
|
+
|
|
35
|
+
Each override re-implements the simple ``start``-advancing loop of
|
|
36
|
+
the synchronous counterpart but ``await``\\ s the underlying
|
|
37
|
+
``search_*`` call so it runs on the event loop rather than inside a
|
|
38
|
+
worker-thread-dispatched ``next()`` call where the coroutine would
|
|
39
|
+
be dropped on the floor.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
async def iter_search_tickets(
|
|
43
|
+
self,
|
|
44
|
+
rsql_filter: str = "",
|
|
45
|
+
*,
|
|
46
|
+
batch_size: int = 50,
|
|
47
|
+
sort: str | None = None,
|
|
48
|
+
fields: tuple[str, ...] = (),
|
|
49
|
+
) -> AsyncIterator[list[GetTicket]]:
|
|
50
|
+
"""Yield successive pages of GLPI tickets until exhausted.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
rsql_filter : str, optional
|
|
55
|
+
Raw RSQL filter forwarded as the ``filter`` query parameter.
|
|
56
|
+
Empty by default, which lists every visible ticket.
|
|
57
|
+
batch_size : int, optional
|
|
58
|
+
Number of records requested per page (default 50).
|
|
59
|
+
sort : str | None, optional
|
|
60
|
+
``sort`` query parameter forwarded as-is to each page request.
|
|
61
|
+
fields : tuple[str, ...], optional
|
|
62
|
+
Restricted set of contract field names to request.
|
|
63
|
+
|
|
64
|
+
Yields
|
|
65
|
+
------
|
|
66
|
+
list[GetTicket]
|
|
67
|
+
One page of tickets per iteration. The last yielded batch may
|
|
68
|
+
be shorter than ``batch_size``.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
start = 0
|
|
72
|
+
while True:
|
|
73
|
+
batch: list[GetTicket] = await self.search_tickets( # type: ignore[attr-defined]
|
|
74
|
+
rsql_filter,
|
|
75
|
+
limit=batch_size,
|
|
76
|
+
start=start,
|
|
77
|
+
sort=sort,
|
|
78
|
+
fields=fields,
|
|
79
|
+
)
|
|
80
|
+
if batch:
|
|
81
|
+
yield batch
|
|
82
|
+
if len(batch) < batch_size:
|
|
83
|
+
break
|
|
84
|
+
start += batch_size
|
|
85
|
+
|
|
86
|
+
async def iter_search_users(
|
|
87
|
+
self,
|
|
88
|
+
rsql_filter: str = "",
|
|
89
|
+
*,
|
|
90
|
+
batch_size: int = 50,
|
|
91
|
+
skip_entity: bool = False,
|
|
92
|
+
) -> AsyncIterator[list[GetUser]]:
|
|
93
|
+
"""Yield successive pages of GLPI users until exhausted.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
rsql_filter : str, optional
|
|
98
|
+
Raw RSQL filter forwarded as the ``filter`` query parameter.
|
|
99
|
+
Empty by default, which lists every visible user.
|
|
100
|
+
batch_size : int, optional
|
|
101
|
+
Number of records requested per page (default 50).
|
|
102
|
+
skip_entity : bool, optional
|
|
103
|
+
When ``True`` the ``GLPI-Entity`` header is omitted so the
|
|
104
|
+
search spans every entity the caller has access to.
|
|
105
|
+
|
|
106
|
+
Yields
|
|
107
|
+
------
|
|
108
|
+
list[GetUser]
|
|
109
|
+
One page of users per iteration. The last yielded batch may
|
|
110
|
+
be shorter than ``batch_size``.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
start = 0
|
|
114
|
+
while True:
|
|
115
|
+
batch: list[GetUser] = await self.search_users( # type: ignore[attr-defined]
|
|
116
|
+
rsql_filter,
|
|
117
|
+
limit=batch_size,
|
|
118
|
+
start=start,
|
|
119
|
+
skip_entity=skip_entity,
|
|
120
|
+
)
|
|
121
|
+
if batch:
|
|
122
|
+
yield batch
|
|
123
|
+
if len(batch) < batch_size:
|
|
124
|
+
break
|
|
125
|
+
start += batch_size
|
|
126
|
+
|
|
127
|
+
async def iter_search_entities(
|
|
128
|
+
self,
|
|
129
|
+
rsql_filter: str = "",
|
|
130
|
+
*,
|
|
131
|
+
batch_size: int = 50,
|
|
132
|
+
) -> AsyncIterator[list[GetEntity]]:
|
|
133
|
+
"""Yield successive pages of GLPI entities until exhausted.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
rsql_filter : str, optional
|
|
138
|
+
Raw RSQL filter forwarded as the ``filter`` query parameter.
|
|
139
|
+
Empty by default, which lists every accessible entity.
|
|
140
|
+
batch_size : int, optional
|
|
141
|
+
Number of records requested per page (default 50).
|
|
142
|
+
|
|
143
|
+
Yields
|
|
144
|
+
------
|
|
145
|
+
list[GetEntity]
|
|
146
|
+
One page of entities per iteration. The last yielded batch
|
|
147
|
+
may be shorter than ``batch_size``.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
start = 0
|
|
151
|
+
while True:
|
|
152
|
+
batch: list[GetEntity] = await self.search_entities( # type: ignore[attr-defined]
|
|
153
|
+
rsql_filter,
|
|
154
|
+
limit=batch_size,
|
|
155
|
+
start=start,
|
|
156
|
+
)
|
|
157
|
+
if batch:
|
|
158
|
+
yield batch
|
|
159
|
+
if len(batch) < batch_size:
|
|
160
|
+
break
|
|
161
|
+
start += batch_size
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
__all__ = ["AsyncPaginationMixin"]
|