anaplan-sdk 0.2.8__tar.gz → 0.2.10__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 (67) hide show
  1. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/.github/workflows/tests.yml +2 -2
  2. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/PKG-INFO +3 -3
  3. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_async_clients/_alm.py +10 -0
  4. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_async_clients/_audit.py +38 -27
  5. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_async_clients/_bulk.py +1 -0
  6. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_clients/_alm.py +10 -0
  7. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_clients/_audit.py +37 -26
  8. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_clients/_bulk.py +1 -0
  9. anaplan_sdk-0.2.10/docs/guides/audit.md +81 -0
  10. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/index.md +3 -0
  11. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/pyproject.toml +7 -5
  12. anaplan_sdk-0.2.10/tests/async/conftest.py +58 -0
  13. anaplan_sdk-0.2.10/tests/async/test_async_alm_client.py +28 -0
  14. anaplan_sdk-0.2.10/tests/async/test_async_audit_client.py +12 -0
  15. anaplan_sdk-0.2.10/tests/async/test_async_client.py +91 -0
  16. anaplan_sdk-0.2.10/tests/async/test_async_transactional_client.py +60 -0
  17. anaplan_sdk-0.2.10/tests/sync/conftest.py +58 -0
  18. anaplan_sdk-0.2.10/tests/sync/test_alm_client.py +28 -0
  19. anaplan_sdk-0.2.10/tests/sync/test_audit_client.py +14 -0
  20. {anaplan_sdk-0.2.8/tests → anaplan_sdk-0.2.10/tests/sync}/test_client.py +23 -60
  21. anaplan_sdk-0.2.10/tests/sync/test_transactional_client.py +58 -0
  22. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/uv.lock +5 -5
  23. anaplan_sdk-0.2.8/docs/guides/audit.md +0 -42
  24. anaplan_sdk-0.2.8/tests/test_alm_client.py +0 -46
  25. anaplan_sdk-0.2.8/tests/test_async_alm_client.py +0 -59
  26. anaplan_sdk-0.2.8/tests/test_async_client.py +0 -153
  27. anaplan_sdk-0.2.8/tests/test_async_transactional_client.py +0 -84
  28. anaplan_sdk-0.2.8/tests/test_transactional_client.py +0 -65
  29. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/.github/dependabot.yml +0 -0
  30. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/.github/workflows/docs.yml +0 -0
  31. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/.github/workflows/lint.yml +0 -0
  32. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/.gitignore +0 -0
  33. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/.pre-commit-config.yaml +0 -0
  34. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/LICENSE +0 -0
  35. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/README.md +0 -0
  36. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/__init__.py +0 -0
  37. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_async_clients/__init__.py +0 -0
  38. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_async_clients/_transactional.py +0 -0
  39. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_auth.py +0 -0
  40. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_base.py +0 -0
  41. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_clients/__init__.py +0 -0
  42. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/_clients/_transactional.py +0 -0
  43. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/exceptions.py +0 -0
  44. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/anaplan_sdk/models.py +0 -0
  45. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/anaplan_explained.md +0 -0
  46. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/api/alm_client.md +0 -0
  47. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/api/async_alm_client.md +0 -0
  48. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/api/async_audit_client.md +0 -0
  49. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/api/async_client.md +0 -0
  50. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/api/async_transactional_client.md +0 -0
  51. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/api/audit_client.md +0 -0
  52. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/api/client.md +0 -0
  53. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/api/exceptions.md +0 -0
  54. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/api/models.md +0 -0
  55. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/api/transactional_client.md +0 -0
  56. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/css/styles.css +0 -0
  57. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/guides/alm.md +0 -0
  58. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/guides/bulk.md +0 -0
  59. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/guides/bulk_vs_transactional.md +0 -0
  60. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/guides/logging.md +0 -0
  61. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/guides/multiple_models.md +0 -0
  62. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/guides/transactional.md +0 -0
  63. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/img/anaplan-overview.webp +0 -0
  64. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/img/anaplan-sdk.webp +0 -0
  65. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/installation.md +0 -0
  66. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/docs/quickstart.md +0 -0
  67. {anaplan_sdk-0.2.8 → anaplan_sdk-0.2.10}/mkdocs.yml +0 -0
@@ -18,7 +18,7 @@ jobs:
18
18
  runs-on: "ubuntu-latest"
19
19
 
20
20
  strategy:
21
- max-parallel: 1
21
+ max-parallel: 4
22
22
  matrix:
23
23
  python-version: [ "3.10.4", "3.11", "3.12", "3.13" ]
24
24
 
@@ -32,4 +32,4 @@ jobs:
32
32
  pip install uv
33
33
  uv sync
34
34
  - name: "Run tests"
35
- run: "uv run python -m pytest -n 6 --dist loadfile tests/"
35
+ run: "uv run python -m pytest -n 8 --dist loadfile tests/"
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anaplan-sdk
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: Provides pythonic access to 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/
8
- Author-email: Vinzenz Klass <vinzenz.klass@ba.valantic.com>
8
+ Author-email: Vinzenz Klass <vinzenz.klass@valantic.com>
9
9
  License-Expression: Apache-2.0
10
10
  License-File: LICENSE
11
- Keywords: anaplan,anaplan alm api,anaplan api,anaplan bulk api,anaplan integration
11
+ Keywords: anaplan,anaplan alm api,anaplan api,anaplan audit api,anaplan bulk api,anaplan integration
12
12
  Requires-Python: >=3.10.4
13
13
  Requires-Dist: cryptography<45.0.0,>=42.0.7
14
14
  Requires-Dist: httpx<1.0.0,>=0.27.0
@@ -1,8 +1,12 @@
1
+ import warnings
2
+
1
3
  import httpx
2
4
 
3
5
  from anaplan_sdk._base import _AsyncBaseClient
4
6
  from anaplan_sdk.models import ModelRevision, Revision, SyncTask, User
5
7
 
8
+ warnings.filterwarnings("always", category=DeprecationWarning)
9
+
6
10
 
7
11
  class _AsyncAlmClient(_AsyncBaseClient):
8
12
  def __init__(self, client: httpx.AsyncClient, model_id: str, retry_count: int) -> None:
@@ -15,6 +19,12 @@ class _AsyncAlmClient(_AsyncBaseClient):
15
19
  Lists all the Users in the authenticated users default tenant.
16
20
  :return: The List of Users.
17
21
  """
22
+ warnings.warn(
23
+ "`list_users()` on the ALM client is deprecated and will be removed in a "
24
+ "future version. Use `list_users()` on the Audit client instead.",
25
+ DeprecationWarning,
26
+ stacklevel=1,
27
+ )
18
28
  return [
19
29
  User.model_validate(e)
20
30
  for e in (await self._get("https://api.anaplan.com/2/0/users")).get("users")
@@ -6,6 +6,7 @@ from typing import Literal
6
6
  import httpx
7
7
 
8
8
  from anaplan_sdk._base import _AsyncBaseClient
9
+ from anaplan_sdk.models import User
9
10
 
10
11
  Event = Literal["all", "byok", "user_activity"]
11
12
 
@@ -17,6 +18,43 @@ class _AsyncAuditClient(_AsyncBaseClient):
17
18
  self._url = "https://audit.anaplan.com/audit/api/1/events"
18
19
  super().__init__(retry_count, client)
19
20
 
21
+ async def list_users(self) -> list[User]:
22
+ """
23
+ Lists all the Users in the authenticated users default tenant.
24
+ :return: The List of Users.
25
+ """
26
+ return [
27
+ User.model_validate(e)
28
+ for e in (await self._get("https://api.anaplan.com/2/0/users")).get("users")
29
+ ]
30
+
31
+ async def get_events(self, days_into_past: int = 30, event_type: Event = "all") -> list:
32
+ """
33
+ Get audit events from Anaplan Audit API.
34
+ :param days_into_past: The nuber of days into the past to get events for. The API provides
35
+ data for up to 30 days.
36
+ :param event_type: The type of events to get.
37
+ :return: A list of audit events.
38
+ """
39
+ total = await self._get_total(days_into_past, event_type)
40
+ if total == 0:
41
+ return []
42
+ if total <= 10_000:
43
+ return await self._get_result_page(days_into_past, event_type)
44
+
45
+ return list(
46
+ chain.from_iterable(
47
+ await gather(
48
+ *(
49
+ self._get_result_page(
50
+ days_into_past, event_type, self._limit, n * self._limit
51
+ )
52
+ for n in range(ceil(total / self._limit))
53
+ )
54
+ )
55
+ )
56
+ )
57
+
20
58
  async def _get_total(self, days_into_past: int = 60, event_type: Event = "all") -> int:
21
59
  return ( # noqa
22
60
  await self._get(
@@ -47,30 +85,3 @@ class _AsyncAuditClient(_AsyncBaseClient):
47
85
  },
48
86
  )
49
87
  ).get("response", [])
50
-
51
- async def get_events(self, days_into_past: int = 30, event_type: Event = "all") -> list:
52
- """
53
- Get audit events from Anaplan Audit API.
54
- :param days_into_past: The nuber of days into the past to get events for. The API provides
55
- data for up to 30 days.
56
- :param event_type: The type of events to get.
57
- :return: A list of audit events.
58
- """
59
- total = await self._get_total(days_into_past, event_type)
60
- if total == 0:
61
- return []
62
- if total <= 10_000:
63
- return await self._get_result_page(days_into_past, event_type)
64
-
65
- return list(
66
- chain.from_iterable(
67
- await gather(
68
- *(
69
- self._get_result_page(
70
- days_into_past, event_type, self._limit, n * self._limit
71
- )
72
- for n in range(ceil(total / self._limit))
73
- )
74
- )
75
- )
76
- )
@@ -133,6 +133,7 @@ class AsyncClient(_AsyncBaseClient):
133
133
  client._transactional_client = _AsyncTransactionalClient(
134
134
  existing._client, model_id, existing._retry_count
135
135
  )
136
+ client._alm_client = _AsyncAlmClient(existing._client, model_id, existing._retry_count)
136
137
  return client
137
138
 
138
139
  @property
@@ -1,8 +1,12 @@
1
+ import warnings
2
+
1
3
  import httpx
2
4
 
3
5
  from anaplan_sdk._base import _BaseClient
4
6
  from anaplan_sdk.models import ModelRevision, Revision, SyncTask, User
5
7
 
8
+ warnings.filterwarnings("always", category=DeprecationWarning)
9
+
6
10
 
7
11
  class _AlmClient(_BaseClient):
8
12
  def __init__(self, client: httpx.Client, model_id: str, retry_count: int) -> None:
@@ -15,6 +19,12 @@ class _AlmClient(_BaseClient):
15
19
  Lists all the Users in the authenticated users default tenant.
16
20
  :return: The List of Users.
17
21
  """
22
+ warnings.warn(
23
+ "`list_users()` on the ALM client is deprecated and will be removed in a "
24
+ "future version. Use `list_users()` on the Audit client instead.",
25
+ DeprecationWarning,
26
+ stacklevel=1,
27
+ )
18
28
  return [
19
29
  User.model_validate(e)
20
30
  for e in self._get("https://api.anaplan.com/2/0/users").get("users")
@@ -5,6 +5,7 @@ from typing import Literal
5
5
  import httpx
6
6
 
7
7
  from anaplan_sdk._base import _BaseClient
8
+ from anaplan_sdk.models import User
8
9
 
9
10
  Event = Literal["all", "byok", "user_activity"]
10
11
 
@@ -17,6 +18,42 @@ class _AuditClient(_BaseClient):
17
18
  self._url = "https://audit.anaplan.com/audit/api/1/events"
18
19
  super().__init__(retry_count, client)
19
20
 
21
+ def list_users(self) -> list[User]:
22
+ """
23
+ Lists all the Users in the authenticated users default tenant.
24
+ :return: The List of Users.
25
+ """
26
+ return [
27
+ User.model_validate(e)
28
+ for e in self._get("https://api.anaplan.com/2/0/users").get("users")
29
+ ]
30
+
31
+ def get_events(self, days_into_past: int = 30, event_type: Event = "all") -> list:
32
+ """
33
+ Get audit events from Anaplan Audit API.
34
+ :param days_into_past: The nuber of days into the past to get events for. The API provides
35
+ data for up to 30 days.
36
+ :param event_type: The type of events to get.
37
+ :return: A list of audit events.
38
+ """
39
+ total = self._get_total(days_into_past, event_type)
40
+ if total == 0:
41
+ return []
42
+ if total <= 10_000:
43
+ return self._get_result_page(days_into_past, event_type)
44
+
45
+ from concurrent.futures import ThreadPoolExecutor
46
+
47
+ with ThreadPoolExecutor(max_workers=self._thread_count) as executor:
48
+ futures = [
49
+ executor.submit(
50
+ self._get_result_page, days_into_past, event_type, self._limit, n * self._limit
51
+ )
52
+ for n in range(ceil(total / self._limit))
53
+ ]
54
+ results = [future.result() for future in futures]
55
+ return list(chain.from_iterable(results))
56
+
20
57
  def _get_total(self, days_into_past: int = 60, event_type: Event = "all") -> int:
21
58
  return ( # noqa
22
59
  self._get(
@@ -47,29 +84,3 @@ class _AuditClient(_BaseClient):
47
84
  },
48
85
  )
49
86
  ).get("response", [])
50
-
51
- def get_events(self, days_into_past: int = 30, event_type: Event = "all") -> list:
52
- """
53
- Get audit events from Anaplan Audit API.
54
- :param days_into_past: The nuber of days into the past to get events for. The API provides
55
- data for up to 30 days.
56
- :param event_type: The type of events to get.
57
- :return: A list of audit events.
58
- """
59
- total = self._get_total(days_into_past, event_type)
60
- if total == 0:
61
- return []
62
- if total <= 10_000:
63
- return self._get_result_page(days_into_past, event_type)
64
-
65
- from concurrent.futures import ThreadPoolExecutor
66
-
67
- with ThreadPoolExecutor(max_workers=self._thread_count) as executor:
68
- futures = [
69
- executor.submit(
70
- self._get_result_page, days_into_past, event_type, self._limit, n * self._limit
71
- )
72
- for n in range(ceil(total / self._limit))
73
- ]
74
- results = [future.result() for future in futures]
75
- return list(chain.from_iterable(results))
@@ -140,6 +140,7 @@ class Client(_BaseClient):
140
140
  client._transactional_client = _TransactionalClient(
141
141
  existing._client, model_id, existing._retry_count
142
142
  )
143
+ client._alm_client = _AlmClient(existing._client, model_id, existing._retry_count)
143
144
  return client
144
145
 
145
146
  @property
@@ -0,0 +1,81 @@
1
+ You can use the Audit API to get fine-grained information about the changes made to any model, usage, user sign-ins,
2
+ most frequently visited pages and much more. The Audit API exposes most the logs collected by Anaplan.
3
+
4
+ For API details refer to
5
+ [the Documentation](https://auditservice.docs.apiary.io/#).
6
+
7
+ ## Usage
8
+
9
+ !!! tip "Tenant Level API"
10
+ Note the absence of `workspace_id` and `model_id` in the Audit API. The Audit API is a tenant level API, meaning it
11
+ is not scoped to a specific workspace or model. You can use the Audit API to get information about all workspaces and
12
+ models for the default tenant of the user you are providing the credentials for.
13
+
14
+ The methods for the Audit API reside in a different namespace for better API navigability and
15
+ comprehensiveness, but are accessible through the same client for convenience. For e.g., you can call
16
+ the `.get_events()` method like so:
17
+
18
+ /// tab | Synchronous
19
+
20
+ ```python
21
+ import anaplan_sdk
22
+
23
+ anaplan = anaplan_sdk.Client(
24
+ certificate="~/certs/anaplan.pem", private_key="~/keys/anaplan.pem"
25
+ )
26
+ events = anaplan.audit.get_events()
27
+ ```
28
+
29
+ ///
30
+ /// tab | Asynchronous
31
+
32
+ ```python
33
+ import anaplan_sdk
34
+
35
+ anaplan = anaplan_sdk.AsyncClient(
36
+ certificate="~/certs/anaplan.pem", private_key="~/keys/anaplan.pem"
37
+ )
38
+ events = await anaplan.audit.get_events()
39
+ ```
40
+
41
+ ///
42
+
43
+ The Audit API also exposes the `list_users()` method to get a list of all users in the workspace. You can for e.g. use
44
+ the two methods in combination to get a list of events with the username using [polars](https://docs.pola.rs):
45
+
46
+ /// tab | Synchronous
47
+
48
+ ```python
49
+ import polars as pl
50
+
51
+ events, users = anaplan.audit.get_events(14), anaplan.audit.list_users()
52
+ df = pl.DataFrame(events, orient="row", infer_schema_length=1_000).join(
53
+ pl.DataFrame(users, orient="row").select(
54
+ pl.col("id").alias("userId"), "first_name", "last_name"
55
+ ),
56
+ on="userId",
57
+ how="left",
58
+ )
59
+ ```
60
+
61
+ ///
62
+ /// tab | Asynchronous
63
+
64
+ ```python
65
+ import polars as pl
66
+
67
+ events, users = await gather(
68
+ anaplan.audit.get_events(14), anaplan.audit.list_users()
69
+ )
70
+ df = pl.DataFrame(events, orient="row", infer_schema_length=1_000).join(
71
+ pl.DataFrame(users, orient="row").select(
72
+ pl.col("id").alias("userId"), "first_name", "last_name"
73
+ ),
74
+ on="userId",
75
+ how="left",
76
+ )
77
+ ```
78
+
79
+ ///
80
+
81
+
@@ -29,3 +29,6 @@ providing both synchronous and asynchronous Clients.
29
29
 
30
30
  Head over to the [Quick Start](quickstart.md) to get started with the basics.
31
31
 
32
+ If you find any issues or feel that this SDK is not adequately covering your use case,
33
+ please [open an issue](https://github.com/VinzenzKlass/anaplan-sdk/issues/new).
34
+
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "anaplan-sdk"
3
- version = "0.2.8"
3
+ version = "0.2.10"
4
4
  description = "Provides pythonic access to the Anaplan API"
5
5
  license = "Apache-2.0"
6
- authors = [{ name = "Vinzenz Klass", email = "vinzenz.klass@ba.valantic.com" }]
6
+ authors = [{ name = "Vinzenz Klass", email = "vinzenz.klass@valantic.com" }]
7
7
  readme = "README.md"
8
- keywords = ["anaplan", "anaplan api", "anaplan bulk api", "anaplan integration", "anaplan alm api"]
8
+ keywords = ["anaplan", "anaplan api", "anaplan bulk api", "anaplan integration", "anaplan alm api", "anaplan audit api"]
9
9
  requires-python = ">=3.10.4"
10
10
  dependencies = [
11
11
  "pydantic>=2.7.2,<3.0.0",
@@ -18,7 +18,7 @@ dev = [
18
18
  "ruff>=0.9.2",
19
19
  "pre-commit>=4.0.1",
20
20
  "pytest>=8.3.3",
21
- "pytest-asyncio>=0.24.0",
21
+ "pytest-asyncio>=0.26.0",
22
22
  "pytest-xdist>=3.6.1",
23
23
  "mkdocs>=1.6.1",
24
24
  "mkdocs-material>=9.5.41",
@@ -76,7 +76,9 @@ skip-magic-trailing-comma = false
76
76
  select = ["E", "F", "B", "I"]
77
77
 
78
78
  [tool.pytest.ini_options]
79
- asyncio_default_fixture_loop_scope = "module"
79
+ asyncio_mode = "auto"
80
+ asyncio_default_fixture_loop_scope = "session"
81
+ asyncio_default_test_loop_scope = "session"
80
82
  minversion = "8.0"
81
83
  addopts = "-ra -q"
82
84
  pythonpath = "anaplan_sdk/"
@@ -0,0 +1,58 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ from os import getenv
5
+
6
+ import pytest
7
+
8
+ import anaplan_sdk
9
+
10
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
11
+ logging.getLogger("httpx").setLevel(logging.ERROR)
12
+ logging.getLogger("anaplan_sdk").setLevel(logging.INFO)
13
+
14
+
15
+ @pytest.fixture(scope="session")
16
+ def client():
17
+ return anaplan_sdk.AsyncClient(
18
+ workspace_id=getenv("ANAPLAN_SDK_TEST_WORKSPACE_ID"),
19
+ model_id=getenv("ANAPLAN_SDK_TEST_MODEL_ID"),
20
+ certificate=getenv("ANAPLAN_SDK_TEST_CERT"),
21
+ private_key=getenv("ANAPLAN_SDK_TEST_PK"),
22
+ retry_count=3,
23
+ )
24
+
25
+
26
+ @pytest.fixture(scope="session")
27
+ def broken_client():
28
+ return anaplan_sdk.AsyncClient(
29
+ workspace_id="random",
30
+ model_id="nonsense",
31
+ certificate=os.getenv("ANAPLAN_SDK_TEST_CERT"),
32
+ private_key=os.getenv("ANAPLAN_SDK_TEST_PK"),
33
+ retry_count=1,
34
+ )
35
+
36
+
37
+ @pytest.fixture(scope="session")
38
+ def test_file():
39
+ py_version = sys.version.split(" ")[0]
40
+ if "3.10" in py_version:
41
+ return 113000000061
42
+ if "3.11" in py_version:
43
+ return 113000000062
44
+ if "3.12" in py_version:
45
+ return 113000000063
46
+ return 113000000064
47
+
48
+
49
+ @pytest.fixture(scope="session")
50
+ def test_action():
51
+ py_version = sys.version.split(" ")[0]
52
+ if "3.10" in py_version:
53
+ return 118000000028
54
+ if "3.11" in py_version:
55
+ return 118000000027
56
+ if "3.12" in py_version:
57
+ return 118000000026
58
+ return 118000000025
@@ -0,0 +1,28 @@
1
+ from anaplan_sdk import AsyncClient
2
+
3
+
4
+ async def test_get_revisions(client: AsyncClient):
5
+ revs = await client.alm.get_revisions()
6
+ assert isinstance(revs, list)
7
+ assert len(revs) > 0
8
+
9
+
10
+ async def test_get_models_for_revision(client: AsyncClient):
11
+ model_revs = await client.alm.get_models_for_revision("44867AAA4DD94C6EB8A23690A0C11DF4")
12
+ assert isinstance(model_revs, list)
13
+ assert len(model_revs) > 0
14
+
15
+
16
+ async def test_get_sync_tasks(client: AsyncClient):
17
+ tasks = await client.alm.get_sync_tasks()
18
+ assert isinstance(tasks, list)
19
+
20
+
21
+ async def test_get_syncable_revisions(client: AsyncClient):
22
+ models = await client.alm.get_syncable_revisions("327F80BA66344A1C84C69AE82C006CDE")
23
+ assert isinstance(models, list)
24
+
25
+
26
+ async def test_get_latest_revision(client: AsyncClient):
27
+ revs = await client.alm.get_latest_revision()
28
+ assert isinstance(revs, list)
@@ -0,0 +1,12 @@
1
+ from anaplan_sdk import AsyncClient, models
2
+
3
+
4
+ async def test_list_users(client: AsyncClient):
5
+ users = await client.audit.list_users()
6
+ assert isinstance(users, list)
7
+ assert isinstance(users[0], models.User)
8
+
9
+
10
+ async def test_events(client: AsyncClient):
11
+ events = await client.audit.get_events(1)
12
+ assert isinstance(events, list)
@@ -0,0 +1,91 @@
1
+ from anaplan_sdk import AsyncClient
2
+ from anaplan_sdk.exceptions import InvalidIdentifierException
3
+
4
+
5
+ async def test_list_workspaces(client: AsyncClient):
6
+ workspaces = await client.list_workspaces()
7
+ assert isinstance(workspaces, list)
8
+ assert len(workspaces) > 0
9
+
10
+
11
+ async def test_broken_list_workspaces_raises_invalid_identifier_error(broken_client):
12
+ try:
13
+ await broken_client.list_workspaces()
14
+ except Exception as error:
15
+ assert isinstance(error, InvalidIdentifierException)
16
+
17
+
18
+ async def test_list_models(client: AsyncClient):
19
+ models = await client.list_models()
20
+ assert isinstance(models, list)
21
+ assert len(models) > 0
22
+
23
+
24
+ async def test_list_actions(client: AsyncClient):
25
+ actions = await client.list_actions()
26
+ assert isinstance(actions, list)
27
+ assert len(actions) > 0
28
+
29
+
30
+ async def test_list_files(client: AsyncClient):
31
+ files = await client.list_files()
32
+ assert isinstance(files, list)
33
+ assert len(files) > 0
34
+
35
+
36
+ async def test_list_processes(client: AsyncClient):
37
+ processes = await client.list_processes()
38
+ assert isinstance(processes, list)
39
+ assert len(processes) > 0
40
+
41
+
42
+ async def test_list_imports(client: AsyncClient):
43
+ imports = await client.list_imports()
44
+ assert isinstance(imports, list)
45
+ assert len(imports) > 0
46
+
47
+
48
+ async def test_list_exports(client: AsyncClient):
49
+ exports = await client.list_exports()
50
+ assert isinstance(exports, list)
51
+ assert len(exports) > 0
52
+
53
+
54
+ async def test_upload_file_stream(client, test_file):
55
+ await client.upload_file_stream(test_file, (i async for i in _async_range(10)))
56
+ out = await client.get_file(test_file)
57
+ assert out == b"0123456789"
58
+
59
+
60
+ async def test_get_file_stream(client, test_file):
61
+ async for chunk in client.get_file_stream(test_file):
62
+ assert isinstance(chunk, bytes)
63
+
64
+
65
+ async def test_upload_and_download_file(client, test_file):
66
+ await client.upload_file(test_file, "Hi!")
67
+ out = await client.get_file(test_file)
68
+ assert out == b"Hi!"
69
+
70
+
71
+ async def test_run_process(client, test_action):
72
+ await client.run_action(test_action)
73
+
74
+
75
+ async def test_invoke_action(client, test_action):
76
+ task_id = await client.invoke_action(test_action)
77
+ assert isinstance(task_id, str)
78
+ assert len(task_id) == 32
79
+
80
+
81
+ async def test_get_task_status(client, test_action):
82
+ task_status = await client.get_task_status(test_action, await client.invoke_action(test_action))
83
+ assert isinstance(task_status, dict)
84
+ assert "progress" in task_status
85
+ assert "creationTime" in task_status
86
+ assert "taskState" in task_status
87
+
88
+
89
+ async def _async_range(count: int):
90
+ for i in range(count):
91
+ yield str(i)
@@ -0,0 +1,60 @@
1
+ from uuid import uuid4
2
+
3
+ from anaplan_sdk import AsyncClient
4
+ from anaplan_sdk.models import InsertionResult, ListMetadata, ModelStatus
5
+
6
+
7
+ async def test_list_workspaces(client: AsyncClient):
8
+ modules = await client.transactional.list_modules()
9
+ assert isinstance(modules, list)
10
+ assert len(modules) > 0
11
+
12
+
13
+ async def test_list_lists(client: AsyncClient):
14
+ lists = await client.transactional.list_lists()
15
+ assert isinstance(lists, list)
16
+ assert len(lists) > 0
17
+
18
+
19
+ async def test_list_line_items(client: AsyncClient):
20
+ items = await client.transactional.list_line_items()
21
+ assert isinstance(items, list)
22
+ assert len(items) > 0
23
+
24
+
25
+ async def test_get_list_items(client: AsyncClient):
26
+ items = await client.transactional.get_list_items(101000000009)
27
+ assert isinstance(items, list)
28
+
29
+
30
+ async def test_get_list_meta(client: AsyncClient):
31
+ meta = await client.transactional.get_list_metadata(101000000009)
32
+ assert isinstance(meta, ListMetadata)
33
+
34
+
35
+ async def test_get_model_status(client: AsyncClient):
36
+ status = await client.transactional.get_model_status()
37
+ assert isinstance(status, ModelStatus)
38
+
39
+
40
+ async def test_list_insertion(client: AsyncClient):
41
+ result = await client.transactional.add_items_to_list(
42
+ 101000000009, [{"code": str(uuid4()), "name": str(uuid4())}]
43
+ )
44
+ assert isinstance(result, InsertionResult)
45
+ assert result.failures == []
46
+ assert result.added == 1
47
+ assert result.ignored == 0
48
+ assert result.total == 1
49
+
50
+
51
+ async def test_delete_list_items(client: AsyncClient):
52
+ code = str(uuid4())
53
+ await client.transactional.add_items_to_list(
54
+ 101000000009, [{"code": code, "name": str(uuid4())}]
55
+ )
56
+ await client.transactional.delete_list_items(101000000009, [{"code": code}])
57
+
58
+
59
+ async def test_reset_list_index(client: AsyncClient):
60
+ await client.transactional.reset_list_index(101000000010)