anaplan-sdk 0.4.4a4__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/.github/workflows/docs.yml +3 -8
  2. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/.github/workflows/lint.yml +1 -1
  3. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/.github/workflows/tests.yml +2 -2
  4. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/.pre-commit-config.yaml +3 -3
  5. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/PKG-INFO +4 -3
  6. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/README.md +2 -1
  7. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/__init__.py +2 -0
  8. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/_async_clients/__init__.py +4 -0
  9. anaplan_sdk-0.5.0/anaplan_sdk/_async_clients/_alm.py +289 -0
  10. anaplan_sdk-0.5.0/anaplan_sdk/_async_clients/_audit.py +67 -0
  11. anaplan_sdk-0.5.0/anaplan_sdk/_async_clients/_bulk.py +609 -0
  12. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/_async_clients/_cloud_works.py +61 -41
  13. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/_async_clients/_cw_flow.py +26 -18
  14. anaplan_sdk-0.5.0/anaplan_sdk/_async_clients/_scim.py +148 -0
  15. anaplan_sdk-0.5.0/anaplan_sdk/_async_clients/_transactional.py +399 -0
  16. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/_auth.py +5 -4
  17. anaplan_sdk-0.5.0/anaplan_sdk/_clients/__init__.py +17 -0
  18. anaplan_sdk-0.5.0/anaplan_sdk/_clients/_alm.py +287 -0
  19. anaplan_sdk-0.5.0/anaplan_sdk/_clients/_audit.py +66 -0
  20. anaplan_sdk-0.5.0/anaplan_sdk/_clients/_bulk.py +605 -0
  21. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/_clients/_cloud_works.py +59 -40
  22. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/_clients/_cw_flow.py +24 -16
  23. anaplan_sdk-0.5.0/anaplan_sdk/_clients/_scim.py +145 -0
  24. anaplan_sdk-0.5.0/anaplan_sdk/_clients/_transactional.py +397 -0
  25. anaplan_sdk-0.5.0/anaplan_sdk/_services.py +277 -0
  26. anaplan_sdk-0.5.0/anaplan_sdk/_utils.py +188 -0
  27. anaplan_sdk-0.5.0/anaplan_sdk/models/__init__.py +96 -0
  28. anaplan_sdk-0.5.0/anaplan_sdk/models/_alm.py +113 -0
  29. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/models/_bulk.py +22 -13
  30. anaplan_sdk-0.5.0/anaplan_sdk/models/_transactional.py +311 -0
  31. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/models/cloud_works.py +6 -2
  32. anaplan_sdk-0.5.0/anaplan_sdk/models/scim.py +282 -0
  33. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/async/async_flows_client.md +1 -1
  34. anaplan_sdk-0.5.0/docs/api/async/async_scim_client.md +11 -0
  35. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/async/async_transactional_client.md +1 -1
  36. anaplan_sdk-0.5.0/docs/api/models/scim.md +7 -0
  37. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/sync/sync_flows_client.md +1 -1
  38. anaplan_sdk-0.5.0/docs/api/sync/sync_scim_client.md +11 -0
  39. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/sync/sync_transactional_client.md +1 -1
  40. anaplan_sdk-0.4.4a4/docs/anaplan_explained.md → anaplan_sdk-0.5.0/docs/concepts.md +6 -3
  41. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/css/styles.css +1 -0
  42. anaplan_sdk-0.5.0/docs/guides/alm.md +293 -0
  43. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/guides/audit.md +3 -3
  44. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/guides/bulk.md +63 -35
  45. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/guides/cloud_works.md +6 -6
  46. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/guides/index.md +4 -1
  47. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/guides/logging.md +7 -1
  48. anaplan_sdk-0.5.0/docs/guides/multiple_models.md +37 -0
  49. anaplan_sdk-0.5.0/docs/guides/scim.md +151 -0
  50. anaplan_sdk-0.5.0/docs/guides/sorting.md +41 -0
  51. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/guides/transactional.md +25 -43
  52. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/index.md +7 -3
  53. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/installation.md +5 -0
  54. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/quickstart.md +13 -8
  55. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/mkdocs.yml +25 -10
  56. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/pyproject.toml +15 -10
  57. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/async/conftest.py +47 -0
  58. anaplan_sdk-0.5.0/tests/async/test_async_alm_client.py +109 -0
  59. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/async/test_async_audit_client.py +1 -1
  60. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/async/test_async_client.py +55 -22
  61. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/async/test_async_cloud_works_client.py +3 -3
  62. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/async/test_async_flows_client.py +2 -2
  63. anaplan_sdk-0.5.0/tests/async/test_async_scim_client.py +73 -0
  64. anaplan_sdk-0.5.0/tests/async/test_async_transactional_client.py +177 -0
  65. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/conftest.py +5 -0
  66. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/sync/conftest.py +47 -0
  67. anaplan_sdk-0.5.0/tests/sync/test_alm_client.py +108 -0
  68. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/sync/test_audit_client.py +1 -1
  69. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/sync/test_client.py +42 -17
  70. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/sync/test_cloud_works_client.py +3 -3
  71. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/tests/sync/test_flows_client.py +2 -2
  72. anaplan_sdk-0.5.0/tests/sync/test_scim_client.py +73 -0
  73. anaplan_sdk-0.5.0/tests/sync/test_transactional_client.py +177 -0
  74. anaplan_sdk-0.5.0/tests/test_filter_expressions.py +66 -0
  75. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/uv.lock +239 -64
  76. anaplan_sdk-0.4.4a4/anaplan_sdk/_async_clients/_alm.py +0 -76
  77. anaplan_sdk-0.4.4a4/anaplan_sdk/_async_clients/_audit.py +0 -57
  78. anaplan_sdk-0.4.4a4/anaplan_sdk/_async_clients/_bulk.py +0 -501
  79. anaplan_sdk-0.4.4a4/anaplan_sdk/_async_clients/_transactional.py +0 -190
  80. anaplan_sdk-0.4.4a4/anaplan_sdk/_base.py +0 -297
  81. anaplan_sdk-0.4.4a4/anaplan_sdk/_clients/__init__.py +0 -6
  82. anaplan_sdk-0.4.4a4/anaplan_sdk/_clients/_alm.py +0 -77
  83. anaplan_sdk-0.4.4a4/anaplan_sdk/_clients/_audit.py +0 -56
  84. anaplan_sdk-0.4.4a4/anaplan_sdk/_clients/_bulk.py +0 -486
  85. anaplan_sdk-0.4.4a4/anaplan_sdk/_clients/_transactional.py +0 -187
  86. anaplan_sdk-0.4.4a4/anaplan_sdk/models/__init__.py +0 -49
  87. anaplan_sdk-0.4.4a4/anaplan_sdk/models/_alm.py +0 -55
  88. anaplan_sdk-0.4.4a4/anaplan_sdk/models/_transactional.py +0 -94
  89. anaplan_sdk-0.4.4a4/docs/guides/alm.md +0 -58
  90. anaplan_sdk-0.4.4a4/docs/guides/multiple_models.md +0 -32
  91. anaplan_sdk-0.4.4a4/tests/async/test_async_alm_client.py +0 -28
  92. anaplan_sdk-0.4.4a4/tests/async/test_async_transactional_client.py +0 -66
  93. anaplan_sdk-0.4.4a4/tests/sync/test_alm_client.py +0 -28
  94. anaplan_sdk-0.4.4a4/tests/sync/test_transactional_client.py +0 -66
  95. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/.github/dependabot.yml +0 -0
  96. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/.gitignore +0 -0
  97. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/LICENSE +0 -0
  98. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/_oauth.py +0 -0
  99. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/exceptions.py +0 -0
  100. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/models/_base.py +0 -0
  101. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/anaplan_sdk/models/flows.py +0 -0
  102. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/async/async_alm_client.md +0 -0
  103. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/async/async_audit_client.md +0 -0
  104. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/async/async_client.md +0 -0
  105. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/async/async_cw_client.md +0 -0
  106. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/async/async_oauth_client.md +0 -0
  107. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/exceptions.md +0 -0
  108. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/models/alm.md +0 -0
  109. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/models/bulk.md +0 -0
  110. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/models/cloud_works.md +0 -0
  111. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/models/flows.md +0 -0
  112. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/models/transactional.md +0 -0
  113. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/sync/sync_alm_client.md +0 -0
  114. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/sync/sync_audit_client.md +0 -0
  115. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/sync/sync_client.md +0 -0
  116. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/sync/sync_cw_client.md +0 -0
  117. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/api/sync/sync_oauth_client.md +0 -0
  118. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/assets/overview.html +0 -0
  119. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/guides/authentication.md +0 -0
  120. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/guides/bulk_vs_transactional.md +0 -0
  121. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/img/anaplan-sdk.webp +0 -0
  122. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/js/assets/hljs.js +0 -0
  123. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/js/assets/hljs.min.js +0 -0
  124. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/js/assets/python.js +0 -0
  125. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/js/assets/python.min.js +0 -0
  126. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/js/highlight.js +0 -0
  127. {anaplan_sdk-0.4.4a4 → anaplan_sdk-0.5.0}/docs/js/highlight.min.js +0 -0
@@ -3,11 +3,13 @@ on:
3
3
  push:
4
4
  branches:
5
5
  - master
6
+ workflow_dispatch:
7
+
6
8
  permissions:
7
9
  contents: write
8
10
  jobs:
9
11
  deploy:
10
- runs-on: ubuntu-latest
12
+ runs-on: ubuntu-22.04
11
13
  steps:
12
14
  - uses: actions/checkout@v4
13
15
  - name: Configure Git Credentials
@@ -17,13 +19,6 @@ jobs:
17
19
  - uses: actions/setup-python@v5
18
20
  with:
19
21
  python-version: 3.13
20
- - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
21
- - uses: actions/cache@v4
22
- with:
23
- key: mkdocs-material-${{ env.cache_id }}
24
- path: .cache
25
- restore-keys: |
26
- mkdocs-material-
27
22
  - name: "Setup"
28
23
  run: |
29
24
  pip install uv
@@ -2,7 +2,7 @@ name: Ruff
2
2
  on: [ push, pull_request ]
3
3
  jobs:
4
4
  ruff:
5
- runs-on: ubuntu-latest
5
+ runs-on: ubuntu-22.04
6
6
  steps:
7
7
  - uses: actions/checkout@v4
8
8
  - uses: astral-sh/ruff-action@v3
@@ -17,7 +17,7 @@ jobs:
17
17
  AZ_STORAGE_SAS_TOKEN: ${{secrets.AZ_STORAGE_SAS_TOKEN}}
18
18
 
19
19
  name: "Python ${{ matrix.python-version }}"
20
- runs-on: "ubuntu-latest"
20
+ runs-on: ubuntu-22.04
21
21
 
22
22
  strategy:
23
23
  max-parallel: 4
@@ -34,4 +34,4 @@ jobs:
34
34
  pip install uv
35
35
  uv sync
36
36
  - name: "Run tests"
37
- run: "uv run python -m pytest -n 12 --dist loadfile tests/"
37
+ run: "uv run python -m pytest -n 15 --dist loadfile tests/"
@@ -1,11 +1,11 @@
1
1
  repos:
2
2
  - repo: https://github.com/astral-sh/uv-pre-commit
3
- rev: 0.7.3
3
+ rev: 0.8.3
4
4
  hooks:
5
5
  - id: uv-lock
6
6
  - repo: https://github.com/astral-sh/ruff-pre-commit
7
- rev: v0.11.9
7
+ rev: v0.12.7
8
8
  hooks:
9
- - id: ruff
9
+ - id: ruff-check
10
10
  args: [ --fix ]
11
11
  - id: ruff-format
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anaplan-sdk
3
- Version: 0.4.4a4
4
- Summary: Streamlined Python Interface for Anaplan
3
+ Version: 0.5.0
4
+ Summary: Streamlined Python Interface for the Anaplan API.
5
5
  Project-URL: Homepage, https://vinzenzklass.github.io/anaplan-sdk/
6
6
  Project-URL: Repository, https://github.com/VinzenzKlass/anaplan-sdk
7
7
  Project-URL: Documentation, https://vinzenzklass.github.io/anaplan-sdk/
@@ -60,7 +60,8 @@ abstractions over all Anaplan APIs, allowing you to focus on business requiremen
60
60
 
61
61
  ## Getting Started
62
62
 
63
- Head over to the [Quick Start](quickstart.md) for basic usage instructions and examples.
63
+ Head over to the [Quick Start](https://vinzenzklass.github.io/anaplan-sdk/quickstart/) for basic usage instructions and
64
+ examples.
64
65
 
65
66
  ## Contributing
66
67
 
@@ -38,7 +38,8 @@ abstractions over all Anaplan APIs, allowing you to focus on business requiremen
38
38
 
39
39
  ## Getting Started
40
40
 
41
- Head over to the [Quick Start](quickstart.md) for basic usage instructions and examples.
41
+ Head over to the [Quick Start](https://vinzenzklass.github.io/anaplan-sdk/quickstart/) for basic usage instructions and
42
+ examples.
42
43
 
43
44
  ## Contributing
44
45
 
@@ -2,6 +2,7 @@ from ._async_clients import AsyncClient
2
2
  from ._auth import AnaplanLocalOAuth, AnaplanRefreshTokenAuth
3
3
  from ._clients import Client
4
4
  from ._oauth import AsyncOauth, Oauth
5
+ from .models.scim import field
5
6
 
6
7
  __all__ = [
7
8
  "AsyncClient",
@@ -12,4 +13,5 @@ __all__ = [
12
13
  "Oauth",
13
14
  "models",
14
15
  "exceptions",
16
+ "field",
15
17
  ]
@@ -2,6 +2,8 @@ from ._alm import _AsyncAlmClient
2
2
  from ._audit import _AsyncAuditClient
3
3
  from ._bulk import AsyncClient
4
4
  from ._cloud_works import _AsyncCloudWorksClient
5
+ from ._cw_flow import _AsyncFlowClient
6
+ from ._scim import _AsyncScimClient
5
7
  from ._transactional import _AsyncTransactionalClient
6
8
 
7
9
  __all__ = [
@@ -9,5 +11,7 @@ __all__ = [
9
11
  "_AsyncAlmClient",
10
12
  "_AsyncAuditClient",
11
13
  "_AsyncCloudWorksClient",
14
+ "_AsyncFlowClient",
12
15
  "_AsyncTransactionalClient",
16
+ "_AsyncScimClient",
13
17
  ]
@@ -0,0 +1,289 @@
1
+ import logging
2
+ from typing import Literal, overload
3
+
4
+ from anaplan_sdk._services import _AsyncHttpService
5
+ from anaplan_sdk._utils import sort_params
6
+ from anaplan_sdk.exceptions import AnaplanActionError
7
+ from anaplan_sdk.models import (
8
+ ModelRevision,
9
+ ReportTask,
10
+ Revision,
11
+ SummaryReport,
12
+ SyncTask,
13
+ TaskSummary,
14
+ )
15
+
16
+ logger = logging.getLogger("anaplan_sdk")
17
+
18
+
19
+ class _AsyncAlmClient:
20
+ def __init__(self, http: _AsyncHttpService, model_id: str) -> None:
21
+ self._http = http
22
+ self._model_id = model_id
23
+ self._url = f"https://api.anaplan.com/2/0/models/{model_id}"
24
+
25
+ async def change_model_status(self, status: Literal["online", "offline"]) -> None:
26
+ """
27
+ Use this call to change the status of a model.
28
+ :param status: The status of the model. Can be either "online" or "offline".
29
+ """
30
+ logger.info(f"Changed model status to '{status}' for model {self._model_id}.")
31
+ await self._http.put(f"{self._url}/onlineStatus", json={"status": status})
32
+
33
+ async def get_revisions(
34
+ self,
35
+ sort_by: Literal["id", "name", "applied_on", "created_on"] | None = None,
36
+ descending: bool = False,
37
+ ) -> list[Revision]:
38
+ """
39
+ Use this call to return a list of revisions for a specific model.
40
+ :param sort_by: The field to sort the results by.
41
+ :param descending: If True, the results will be sorted in descending order.
42
+ :return: A list of revisions for a specific model.
43
+ """
44
+ res = await self._http.get_paginated(
45
+ f"{self._url}/alm/revisions", "revisions", params=sort_params(sort_by, descending)
46
+ )
47
+ return [Revision.model_validate(e) for e in res]
48
+
49
+ async def get_latest_revision(self) -> Revision | None:
50
+ """
51
+ Use this call to return the latest revision for a specific model. The response is in the
52
+ same format as in Getting a list of syncable revisions between two models.
53
+
54
+ If a revision exists, the return list should contain one element only which is the
55
+ latest revision.
56
+ :return: The latest revision for a specific model, or None if no revisions exist.
57
+ """
58
+ res = (await self._http.get(f"{self._url}/alm/latestRevision")).get("revisions")
59
+ return Revision.model_validate(res[0]) if res else None
60
+
61
+ async def get_syncable_revisions(self, source_model_id: str) -> list[Revision]:
62
+ """
63
+ Use this call to return the list of revisions from your source model that can be
64
+ synchronized to your target model.
65
+
66
+ The returned list displays in descending order, by creation date and time. This is
67
+ consistent with how revisions are displayed in the user interface (UI).
68
+ :param source_model_id: The ID of the source model.
69
+ :return: A list of revisions that can be synchronized to the target model.
70
+ """
71
+ res = await self._http.get(
72
+ f"{self._url}/alm/syncableRevisions?sourceModelId={source_model_id}"
73
+ )
74
+ return [Revision.model_validate(e) for e in res.get("revisions", [])]
75
+
76
+ async def create_revision(self, name: str, description: str) -> Revision:
77
+ """
78
+ Create a new revision for the model.
79
+ :param name: The name (title) of the revision.
80
+ :param description: The description of the revision.
81
+ :return: The created Revision Info.
82
+ """
83
+ res = await self._http.post(
84
+ f"{self._url}/alm/revisions", json={"name": name, "description": description}
85
+ )
86
+ rev = Revision.model_validate(res["revision"])
87
+ logger.info(f"Created revision '{name} ({rev.id})'for model {self._model_id}.")
88
+ return rev
89
+
90
+ async def get_sync_tasks(self) -> list[TaskSummary]:
91
+ """
92
+ List the sync tasks for a target mode. The returned the tasks are either in progress, or
93
+ they completed within the last 48 hours.
94
+ :return: A list of sync tasks in descending order of creation time.
95
+ """
96
+ res = await self._http.get(f"{self._url}/alm/syncTasks")
97
+ return [TaskSummary.model_validate(e) for e in res.get("tasks", [])]
98
+
99
+ async def get_sync_task(self, task_id: str) -> SyncTask:
100
+ """
101
+ Get the information for a specific sync task.
102
+ :param task_id: The ID of the sync task.
103
+ :return: The sync task information.
104
+ """
105
+ res = await self._http.get(f"{self._url}/alm/syncTasks/{task_id}")
106
+ return SyncTask.model_validate(res["task"])
107
+
108
+ async def sync_models(
109
+ self,
110
+ source_revision_id: str,
111
+ source_model_id: str,
112
+ target_revision_id: str,
113
+ wait_for_completion: bool = True,
114
+ ) -> SyncTask:
115
+ """
116
+ Create a synchronization task between two revisions. This will synchronize the
117
+ source revision of the source model to the target revision of the target model. This will
118
+ fail if the source revision is incompatible with the target revision.
119
+ :param source_revision_id: The ID of the source revision.
120
+ :param source_model_id: The ID of the source model.
121
+ :param target_revision_id: The ID of the target revision.
122
+ :param wait_for_completion: If True, the method will poll the task status and not return
123
+ until the task is complete. If False, it will spawn the task and return immediately.
124
+ :return: The created sync task.
125
+ """
126
+ payload = {
127
+ "sourceRevisionId": source_revision_id,
128
+ "sourceModelId": source_model_id,
129
+ "targetRevisionId": target_revision_id,
130
+ }
131
+ res = await self._http.post(f"{self._url}/alm/syncTasks", json=payload)
132
+ task = await self.get_sync_task(res["task"]["taskId"])
133
+ logger.info(
134
+ f"Started sync task '{task.id}' from Model '{source_model_id}' "
135
+ f"(Revision '{source_revision_id}') to Model '{self._model_id}'."
136
+ )
137
+ if not wait_for_completion:
138
+ return task
139
+ task = await self._http.poll_task(self.get_sync_task, task.id)
140
+ if not task.result.successful:
141
+ msg = f"Sync task {task.id} completed with errors: {task.result.error}."
142
+ logger.error(msg)
143
+ raise AnaplanActionError(msg)
144
+ logger.info(f"Sync task {task.id} completed successfully.")
145
+ return task
146
+
147
+ async def get_models_for_revision(self, revision_id: str) -> list[ModelRevision]:
148
+ """
149
+ Use this call when you need a list of the models that had a specific revision applied
150
+ to them.
151
+ :param revision_id: The ID of the revision.
152
+ :return: A list of models that had a specific revision applied to them.
153
+ """
154
+ res = await self._http.get(f"{self._url}/alm/revisions/{revision_id}/appliedToModels")
155
+ return [ModelRevision.model_validate(e) for e in res.get("appliedToModels", [])]
156
+
157
+ async def create_comparison_report(
158
+ self,
159
+ source_revision_id: str,
160
+ source_model_id: str,
161
+ target_revision_id: str,
162
+ wait_for_completion: bool = True,
163
+ ) -> ReportTask:
164
+ """
165
+ Generate a full comparison report between two revisions. This will list all the changes made
166
+ to the source revision compared to the target revision.
167
+ :param source_revision_id: The ID of the source revision.
168
+ :param source_model_id: The ID of the source model.
169
+ :param target_revision_id: The ID of the target revision.
170
+ :param wait_for_completion: If True, the method will poll the task status and not return
171
+ until the task is complete. If False, it will spawn the task and return immediately.
172
+ :return: The created report task summary.
173
+ """
174
+ payload = {
175
+ "sourceRevisionId": source_revision_id,
176
+ "sourceModelId": source_model_id,
177
+ "targetRevisionId": target_revision_id,
178
+ }
179
+ res = await self._http.post(f"{self._url}/alm/comparisonReportTasks", json=payload)
180
+ task = await self.get_comparison_report_task(res["task"]["taskId"])
181
+ logger.info(
182
+ f"Started Comparison Report task '{task.id}' between Model '{source_model_id}' "
183
+ f"(Revision '{source_revision_id}') and Model '{self._model_id}'."
184
+ )
185
+ if not wait_for_completion:
186
+ return task
187
+ task = await self._http.poll_task(self.get_comparison_report_task, task.id)
188
+ if not task.result.successful:
189
+ msg = f"Comparison Report task {task.id} completed with errors: {task.result.error}."
190
+ logger.error(msg)
191
+ raise AnaplanActionError(msg)
192
+ logger.info(f"Comparison Report task {task.id} completed successfully.")
193
+ return task
194
+
195
+ async def get_comparison_report_task(self, task_id: str) -> ReportTask:
196
+ """
197
+ Get the task information for a comparison report task.
198
+ :param task_id: The ID of the comparison report task.
199
+ :return: The report task information.
200
+ """
201
+ res = await self._http.get(f"{self._url}/alm/comparisonReportTasks/{task_id}")
202
+ return ReportTask.model_validate(res["task"])
203
+
204
+ async def get_comparison_report(self, task: ReportTask) -> bytes:
205
+ """
206
+ Get the report for a specific task.
207
+ :param task: The report task object containing the task ID.
208
+ :return: The binary content of the comparison report.
209
+ """
210
+ return await self._http.get_binary(
211
+ f"{self._url}/alm/comparisonReports/"
212
+ f"{task.result.target_revision_id}/{task.result.source_revision_id}"
213
+ )
214
+
215
+ @overload
216
+ async def create_comparison_summary(
217
+ self,
218
+ source_revision_id: str,
219
+ source_model_id: str,
220
+ target_revision_id: str,
221
+ wait_for_completion: Literal[True] = True,
222
+ ) -> SummaryReport: ...
223
+
224
+ @overload
225
+ async def create_comparison_summary(
226
+ self,
227
+ source_revision_id: str,
228
+ source_model_id: str,
229
+ target_revision_id: str,
230
+ wait_for_completion: Literal[False] = False,
231
+ ) -> ReportTask: ...
232
+
233
+ async def create_comparison_summary(
234
+ self,
235
+ source_revision_id: str,
236
+ source_model_id: str,
237
+ target_revision_id: str,
238
+ wait_for_completion: bool = True,
239
+ ) -> ReportTask | SummaryReport:
240
+ """
241
+ Generate a comparison summary between two revisions.
242
+ :param source_revision_id: The ID of the source revision.
243
+ :param source_model_id: The ID of the source model.
244
+ :param target_revision_id: The ID of the target revision.
245
+ :param wait_for_completion: If True, the method will poll the task status and not return
246
+ until the task is complete. If False, it will spawn the task and return immediately.
247
+ :return: The created summary task or the summary report, if `wait_for_completion` is True.
248
+ """
249
+ payload = {
250
+ "sourceRevisionId": source_revision_id,
251
+ "sourceModelId": source_model_id,
252
+ "targetRevisionId": target_revision_id,
253
+ }
254
+ res = await self._http.post(f"{self._url}/alm/summaryReportTasks", json=payload)
255
+ task = await self.get_comparison_summary_task(res["task"]["taskId"])
256
+ logger.info(
257
+ f"Started Comparison Summary task '{task.id}' between Model '{source_model_id}' "
258
+ f"(Revision '{source_revision_id}') and Model '{self._model_id}'."
259
+ )
260
+ if not wait_for_completion:
261
+ return task
262
+ task = await self._http.poll_task(self.get_comparison_summary_task, task.id)
263
+ if not task.result.successful:
264
+ msg = f"Comparison Summary task {task.id} completed with errors: {task.result.error}."
265
+ logger.error(msg)
266
+ raise AnaplanActionError(msg)
267
+ logger.info(f"Comparison Summary task {task.id} completed successfully.")
268
+ return await self.get_comparison_summary(task)
269
+
270
+ async def get_comparison_summary_task(self, task_id: str) -> ReportTask:
271
+ """
272
+ Get the task information for a comparison summary task.
273
+ :param task_id: The ID of the comparison summary task.
274
+ :return: The report task information.
275
+ """
276
+ res = await self._http.get(f"{self._url}/alm/summaryReportTasks/{task_id}")
277
+ return ReportTask.model_validate(res["task"])
278
+
279
+ async def get_comparison_summary(self, task: ReportTask) -> SummaryReport:
280
+ """
281
+ Get the comparison summary for a specific task.
282
+ :param task: The summary task object containing the task ID.
283
+ :return: The binary content of the comparison summary.
284
+ """
285
+ res = await self._http.get(
286
+ f"{self._url}/alm/summaryReports/"
287
+ f"{task.result.target_revision_id}/{task.result.source_revision_id}"
288
+ )
289
+ return SummaryReport.model_validate(res["summaryReport"])
@@ -0,0 +1,67 @@
1
+ from typing import Any, Literal
2
+
3
+ from anaplan_sdk._services import _AsyncHttpService
4
+ from anaplan_sdk._utils import sort_params
5
+ from anaplan_sdk.models import User
6
+
7
+ Event = Literal["all", "byok", "user_activity"]
8
+ UserSortBy = Literal["first_name", "last_name", "email", "active", "last_login_date"] | None
9
+
10
+
11
+ class _AsyncAuditClient:
12
+ def __init__(self, http: _AsyncHttpService) -> None:
13
+ self._http = http
14
+ self._url = "https://audit.anaplan.com/audit/api/1/events"
15
+
16
+ async def get_users(
17
+ self,
18
+ search_pattern: str | None = None,
19
+ sort_by: UserSortBy = None,
20
+ descending: bool = False,
21
+ ) -> list[User]:
22
+ """
23
+ Lists all the Users in the authenticated users default tenant.
24
+ :param search_pattern: **Caution: This is an undocumented Feature and may behave
25
+ unpredictably. It requires the Tenant Admin role. For non-admin users, it is
26
+ ignored.** Optionally filter for specific users. When provided,
27
+ case-insensitive matches users with emails or names containing this string.
28
+ You can use the wildcards `%` for 0-n characters, and `_` for exactly 1 character.
29
+ When None (default), returns all users.
30
+ :param sort_by: The field to sort the results by.
31
+ :param descending: If True, the results will be sorted in descending order.
32
+ :return: The List of Users.
33
+ """
34
+ params = sort_params(sort_by, descending)
35
+ if search_pattern:
36
+ params["s"] = search_pattern
37
+ res = await self._http.get_paginated(
38
+ "https://api.anaplan.com/2/0/users", "users", params=params
39
+ )
40
+ return [User.model_validate(e) for e in res]
41
+
42
+ async def get_user(self, user_id: str = "me") -> User:
43
+ """
44
+ Retrieves information about the specified user, or the authenticated user if none specified.
45
+ :return: The requested or currently authenticated User.
46
+ """
47
+ return User.model_validate(
48
+ (await self._http.get(f"https://api.anaplan.com/2/0/users/{user_id}")).get("user")
49
+ )
50
+
51
+ async def get_events(
52
+ self, days_into_past: int = 30, event_type: Event = "all"
53
+ ) -> list[dict[str, Any]]:
54
+ """
55
+ Get audit events from Anaplan Audit API.
56
+ :param days_into_past: The nuber of days into the past to get events for. The API provides
57
+ data for up to 30 days.
58
+ :param event_type: The type of events to get.
59
+ :return: A list of log entries, each containing a dictionary with event details.
60
+ """
61
+ return list(
62
+ await self._http.get_paginated(
63
+ self._url,
64
+ "response",
65
+ params={"type": event_type, "intervalInHours": days_into_past * 24},
66
+ )
67
+ )