anaplan-sdk 0.5.0a5__tar.gz → 0.5.1__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.
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/.github/workflows/tests.yml +1 -1
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/PKG-INFO +5 -3
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/README.md +4 -2
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/__init__.py +2 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_async_clients/__init__.py +4 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_async_clients/_alm.py +2 -1
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_async_clients/_audit.py +2 -2
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_async_clients/_bulk.py +15 -3
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_async_clients/_cloud_works.py +2 -2
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_async_clients/_cw_flow.py +2 -1
- anaplan_sdk-0.5.1/anaplan_sdk/_async_clients/_scim.py +148 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_async_clients/_transactional.py +2 -2
- anaplan_sdk-0.5.1/anaplan_sdk/_clients/__init__.py +17 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_clients/_alm.py +2 -1
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_clients/_audit.py +2 -1
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_clients/_bulk.py +15 -3
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_clients/_cloud_works.py +2 -2
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_clients/_cw_flow.py +2 -1
- anaplan_sdk-0.5.1/anaplan_sdk/_clients/_scim.py +145 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_clients/_transactional.py +2 -2
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_services.py +20 -207
- anaplan_sdk-0.5.1/anaplan_sdk/_utils.py +188 -0
- anaplan_sdk-0.5.1/anaplan_sdk/models/scim.py +282 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/async/async_flows_client.md +1 -1
- anaplan_sdk-0.5.1/docs/api/async/async_scim_client.md +11 -0
- anaplan_sdk-0.5.1/docs/api/models/scim.md +7 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/sync/sync_flows_client.md +1 -1
- anaplan_sdk-0.5.1/docs/api/sync/sync_scim_client.md +11 -0
- anaplan_sdk-0.5.1/docs/guides/scim.md +151 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/index.md +1 -1
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/mkdocs.yml +7 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/pyproject.toml +1 -1
- anaplan_sdk-0.5.1/tests/async/test_async_scim_client.py +73 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/conftest.py +5 -0
- anaplan_sdk-0.5.1/tests/sync/test_scim_client.py +73 -0
- anaplan_sdk-0.5.1/tests/test_filter_expressions.py +66 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/uv.lock +1 -1
- anaplan_sdk-0.5.0a5/anaplan_sdk/_clients/__init__.py +0 -6
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/.github/dependabot.yml +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/.github/workflows/docs.yml +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/.github/workflows/lint.yml +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/.gitignore +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/.pre-commit-config.yaml +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/LICENSE +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_auth.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/_oauth.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/exceptions.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/models/__init__.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/models/_alm.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/models/_base.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/models/_bulk.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/models/_transactional.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/models/cloud_works.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/anaplan_sdk/models/flows.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/async/async_alm_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/async/async_audit_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/async/async_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/async/async_cw_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/async/async_oauth_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/async/async_transactional_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/exceptions.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/models/alm.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/models/bulk.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/models/cloud_works.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/models/flows.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/models/transactional.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/sync/sync_alm_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/sync/sync_audit_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/sync/sync_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/sync/sync_cw_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/sync/sync_oauth_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/api/sync/sync_transactional_client.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/assets/overview.html +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/concepts.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/css/styles.css +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/alm.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/audit.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/authentication.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/bulk.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/bulk_vs_transactional.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/cloud_works.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/index.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/logging.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/multiple_models.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/sorting.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/guides/transactional.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/img/anaplan-sdk.webp +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/installation.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/js/assets/hljs.js +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/js/assets/hljs.min.js +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/js/assets/python.js +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/js/assets/python.min.js +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/js/highlight.js +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/js/highlight.min.js +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/docs/quickstart.md +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/async/conftest.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/async/test_async_alm_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/async/test_async_audit_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/async/test_async_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/async/test_async_cloud_works_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/async/test_async_flows_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/async/test_async_transactional_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/sync/conftest.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/sync/test_alm_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/sync/test_audit_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/sync/test_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/sync/test_cloud_works_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/sync/test_flows_client.py +0 -0
- {anaplan_sdk-0.5.0a5 → anaplan_sdk-0.5.1}/tests/sync/test_transactional_client.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: anaplan-sdk
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.1
|
4
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
|
@@ -29,7 +29,7 @@ Description-Content-Type: text/markdown
|
|
29
29
|
</h3>
|
30
30
|
|
31
31
|
<p align="center" style="font-size: 1.2rem; font-weight: 300; margin: 15px 0">
|
32
|
-
Streamlined Python Interface for Anaplan
|
32
|
+
Streamlined Python Interface for the Anaplan API.
|
33
33
|
</p>
|
34
34
|
|
35
35
|
<div align="center">
|
@@ -45,9 +45,11 @@ Description-Content-Type: text/markdown
|
|
45
45
|
</div>
|
46
46
|
|
47
47
|
---
|
48
|
+
Streamlined Python Interface for the Anaplan API. Get up and running with the Anaplan API in minutes.
|
48
49
|
|
49
50
|
Anaplan SDK is an independent, unofficial project providing pythonic access to Anaplan. It delivers high-level
|
50
|
-
abstractions over all Anaplan
|
51
|
+
abstractions over all parts of the Anaplan API, allowing you to focus on business requirements rather than
|
52
|
+
implementation details.
|
51
53
|
|
52
54
|
## Key Features
|
53
55
|
|
@@ -7,7 +7,7 @@
|
|
7
7
|
</h3>
|
8
8
|
|
9
9
|
<p align="center" style="font-size: 1.2rem; font-weight: 300; margin: 15px 0">
|
10
|
-
Streamlined Python Interface for Anaplan
|
10
|
+
Streamlined Python Interface for the Anaplan API.
|
11
11
|
</p>
|
12
12
|
|
13
13
|
<div align="center">
|
@@ -23,9 +23,11 @@
|
|
23
23
|
</div>
|
24
24
|
|
25
25
|
---
|
26
|
+
Streamlined Python Interface for the Anaplan API. Get up and running with the Anaplan API in minutes.
|
26
27
|
|
27
28
|
Anaplan SDK is an independent, unofficial project providing pythonic access to Anaplan. It delivers high-level
|
28
|
-
abstractions over all Anaplan
|
29
|
+
abstractions over all parts of the Anaplan API, allowing you to focus on business requirements rather than
|
30
|
+
implementation details.
|
29
31
|
|
30
32
|
## Key Features
|
31
33
|
|
@@ -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
|
]
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import logging
|
2
2
|
from typing import Literal, overload
|
3
3
|
|
4
|
-
from anaplan_sdk._services import _AsyncHttpService
|
4
|
+
from anaplan_sdk._services import _AsyncHttpService
|
5
|
+
from anaplan_sdk._utils import sort_params
|
5
6
|
from anaplan_sdk.exceptions import AnaplanActionError
|
6
7
|
from anaplan_sdk.models import (
|
7
8
|
ModelRevision,
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from typing import Any, Literal
|
2
2
|
|
3
|
-
from anaplan_sdk._services import _AsyncHttpService
|
3
|
+
from anaplan_sdk._services import _AsyncHttpService
|
4
|
+
from anaplan_sdk._utils import sort_params
|
4
5
|
from anaplan_sdk.models import User
|
5
6
|
|
6
7
|
Event = Literal["all", "byok", "user_activity"]
|
@@ -10,7 +11,6 @@ UserSortBy = Literal["first_name", "last_name", "email", "active", "last_login_d
|
|
10
11
|
class _AsyncAuditClient:
|
11
12
|
def __init__(self, http: _AsyncHttpService) -> None:
|
12
13
|
self._http = http
|
13
|
-
self._limit = 10_000
|
14
14
|
self._url = "https://audit.anaplan.com/audit/api/1/events"
|
15
15
|
|
16
16
|
async def get_users(
|
@@ -7,7 +7,8 @@ import httpx
|
|
7
7
|
from typing_extensions import Self
|
8
8
|
|
9
9
|
from anaplan_sdk._auth import _create_auth
|
10
|
-
from anaplan_sdk._services import _AsyncHttpService
|
10
|
+
from anaplan_sdk._services import _AsyncHttpService
|
11
|
+
from anaplan_sdk._utils import action_url, models_url, sort_params
|
11
12
|
from anaplan_sdk.exceptions import AnaplanActionError, InvalidIdentifierException
|
12
13
|
from anaplan_sdk.models import (
|
13
14
|
Action,
|
@@ -25,6 +26,7 @@ from anaplan_sdk.models import (
|
|
25
26
|
from ._alm import _AsyncAlmClient
|
26
27
|
from ._audit import _AsyncAuditClient
|
27
28
|
from ._cloud_works import _AsyncCloudWorksClient
|
29
|
+
from ._scim import _AsyncScimClient
|
28
30
|
from ._transactional import _AsyncTransactionalClient
|
29
31
|
|
30
32
|
SortBy = Literal["id", "name"] | None
|
@@ -134,7 +136,8 @@ class AsyncClient:
|
|
134
136
|
_AsyncTransactionalClient(self._http, model_id) if model_id else None
|
135
137
|
)
|
136
138
|
self._alm_client = _AsyncAlmClient(self._http, model_id) if model_id else None
|
137
|
-
self.
|
139
|
+
self._audit_client = _AsyncAuditClient(self._http)
|
140
|
+
self._scim_client = _AsyncScimClient(self._http)
|
138
141
|
self._cloud_works = _AsyncCloudWorksClient(self._http)
|
139
142
|
self.upload_chunk_size = upload_chunk_size
|
140
143
|
self.allow_file_creation = allow_file_creation
|
@@ -168,7 +171,7 @@ class AsyncClient:
|
|
168
171
|
The Audit Client provides access to the Anaplan Audit API.
|
169
172
|
For details, see https://vinzenzklass.github.io/anaplan-sdk/guides/audit/.
|
170
173
|
"""
|
171
|
-
return self.
|
174
|
+
return self._audit_client
|
172
175
|
|
173
176
|
@property
|
174
177
|
def cw(self) -> _AsyncCloudWorksClient:
|
@@ -217,6 +220,15 @@ class AsyncClient:
|
|
217
220
|
)
|
218
221
|
return self._alm_client
|
219
222
|
|
223
|
+
@property
|
224
|
+
def scim(self) -> _AsyncScimClient:
|
225
|
+
"""
|
226
|
+
To use the SCIM API, you must be User Admin. The SCIM API allows managing internal users.
|
227
|
+
Visiting users are excluded from the SCIM API.
|
228
|
+
:return: The SCIM Client.
|
229
|
+
"""
|
230
|
+
return self._scim_client
|
231
|
+
|
220
232
|
async def get_workspaces(
|
221
233
|
self,
|
222
234
|
search_pattern: str | None = None,
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import logging
|
2
2
|
from typing import Any, Literal
|
3
3
|
|
4
|
-
from anaplan_sdk._services import
|
5
|
-
|
4
|
+
from anaplan_sdk._services import _AsyncHttpService
|
5
|
+
from anaplan_sdk._utils import (
|
6
6
|
connection_body_payload,
|
7
7
|
construct_payload,
|
8
8
|
integration_payload,
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import logging
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
-
from anaplan_sdk._services import _AsyncHttpService
|
4
|
+
from anaplan_sdk._services import _AsyncHttpService
|
5
|
+
from anaplan_sdk._utils import construct_payload
|
5
6
|
from anaplan_sdk.models.flows import Flow, FlowInput, FlowSummary
|
6
7
|
|
7
8
|
logger = logging.getLogger("anaplan_sdk")
|
@@ -0,0 +1,148 @@
|
|
1
|
+
import logging
|
2
|
+
from asyncio import gather
|
3
|
+
from itertools import chain
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from anaplan_sdk._services import _AsyncHttpService
|
7
|
+
from anaplan_sdk._utils import construct_payload
|
8
|
+
from anaplan_sdk.models.scim import (
|
9
|
+
Operation,
|
10
|
+
ReplaceUserInput,
|
11
|
+
Resource,
|
12
|
+
Schema,
|
13
|
+
ServiceProviderConfig,
|
14
|
+
User,
|
15
|
+
UserInput,
|
16
|
+
field,
|
17
|
+
)
|
18
|
+
|
19
|
+
logger = logging.getLogger("anaplan_sdk")
|
20
|
+
|
21
|
+
|
22
|
+
class _AsyncScimClient:
|
23
|
+
def __init__(self, http: _AsyncHttpService) -> None:
|
24
|
+
self._http = http
|
25
|
+
self._url = "https://api.anaplan.com/scim/1/0/v2"
|
26
|
+
|
27
|
+
async def get_service_provider_config(self) -> ServiceProviderConfig:
|
28
|
+
"""
|
29
|
+
Get the SCIM Service Provider Configuration.
|
30
|
+
:return: The ServiceProviderConfig object describing the available SCIM features.
|
31
|
+
"""
|
32
|
+
res = await self._http.get(f"{self._url}/ServiceProviderConfig")
|
33
|
+
return ServiceProviderConfig.model_validate(res)
|
34
|
+
|
35
|
+
async def get_resource_types(self) -> list[Resource]:
|
36
|
+
"""
|
37
|
+
Get the SCIM Resource Types.
|
38
|
+
:return: A list of Resource objects describing the SCIM resource types.
|
39
|
+
"""
|
40
|
+
res = await self._http.get(f"{self._url}/ResourceTypes")
|
41
|
+
return [Resource.model_validate(e) for e in res.get("Resources", [])]
|
42
|
+
|
43
|
+
async def get_resource_schemas(self) -> list[Schema]:
|
44
|
+
"""
|
45
|
+
Get the SCIM Resource Schemas.
|
46
|
+
:return: A list of Schema objects describing the SCIM resource schemas.
|
47
|
+
"""
|
48
|
+
res = await self._http.get(f"{self._url}/Schemas")
|
49
|
+
return [Schema.model_validate(e) for e in res.get("Resources", [])]
|
50
|
+
|
51
|
+
async def get_users(self, predicate: str | field = None, page_size: int = 100) -> list[User]:
|
52
|
+
"""
|
53
|
+
Get a list of users, optionally filtered by a predicate. Keep in mind that this will only
|
54
|
+
return internal users. To get a list of all users in the tenant, use the `get_users()`
|
55
|
+
in the `audit` namespace instead.
|
56
|
+
:param predicate: A filter predicate to filter the users. This can either be a string,
|
57
|
+
in which case it will be passed as-is, or an expression. Anaplan supports filtering
|
58
|
+
on the following fields: "id", "externalId", "userName", "name.familyName",
|
59
|
+
"name.givenName" and "active". It supports the operators "eq", "ne", "gt", "ge",
|
60
|
+
"lt", "le" and "pr". It supports logical operators "and" and "or", "not" is not
|
61
|
+
supported. It supports grouping with parentheses.
|
62
|
+
:param page_size: The number of users to fetch per page. Values above 100 will error.
|
63
|
+
:return: The internal users optionally matching the filter.
|
64
|
+
"""
|
65
|
+
params: dict[str, int | str] = {"startIndex": 1, "count": page_size}
|
66
|
+
if predicate is not None:
|
67
|
+
_predicate = predicate if isinstance(predicate, str) else str(predicate)
|
68
|
+
logger.debug(f"Searching for users with predicate: {_predicate}")
|
69
|
+
params["filter"] = _predicate
|
70
|
+
res = await self._http.get(f"{self._url}/Users", params=params)
|
71
|
+
users = [User.model_validate(e) for e in res.get("Resources", [])]
|
72
|
+
if (total := res["totalResults"]) <= page_size:
|
73
|
+
return users
|
74
|
+
pages = await gather(
|
75
|
+
*(
|
76
|
+
self._http.get(
|
77
|
+
f"{self._url}/Users", params=(params | {"startIndex": i, "count": page_size})
|
78
|
+
)
|
79
|
+
for i in range(page_size + 1, total + 1, page_size)
|
80
|
+
)
|
81
|
+
)
|
82
|
+
for user in chain(*(p.get("Resources", []) for p in pages)):
|
83
|
+
users.append(User.model_validate(user))
|
84
|
+
return users
|
85
|
+
|
86
|
+
async def get_user(self, user_id: str) -> User:
|
87
|
+
"""
|
88
|
+
Get a user by their ID.
|
89
|
+
:param user_id: The ID of the user to fetch.
|
90
|
+
:return: The User object.
|
91
|
+
"""
|
92
|
+
res = await self._http.get(f"{self._url}/Users/{user_id}")
|
93
|
+
return User.model_validate(res)
|
94
|
+
|
95
|
+
async def add_user(self, user: UserInput | dict[str, Any]) -> User:
|
96
|
+
"""
|
97
|
+
Add a new user to your Anaplan tenant.
|
98
|
+
:param user: The user info to add. Can either be a UserInput object or a dict. If you pass
|
99
|
+
a dict, it will be validated against the UserInput model before sending. If the info
|
100
|
+
you provided is invalid or incomplete, this will raise a pydantic.ValidationError.
|
101
|
+
:return: The created User object.
|
102
|
+
"""
|
103
|
+
res = await self._http.post(f"{self._url}/Users", json=construct_payload(UserInput, user))
|
104
|
+
user = User.model_validate(res)
|
105
|
+
logger.info(f"Added user '{user.user_name}' with ID '{user.id}'.")
|
106
|
+
return user
|
107
|
+
|
108
|
+
async def replace_user(self, user_id: str, user: ReplaceUserInput | dict[str, Any]):
|
109
|
+
"""
|
110
|
+
Replace an existing user with new information. Note that this will replace all fields of the
|
111
|
+
:param user_id: ID of the user to replace.
|
112
|
+
:param user: The new user info. Can either be a ReplaceUserInput object or a dict. If you
|
113
|
+
pass a dict, it will be validated against the ReplaceUserInput model before sending.
|
114
|
+
If the info you provided is invalid or incomplete, this will raise a
|
115
|
+
pydantic.ValidationError.
|
116
|
+
:return: The updated User object.
|
117
|
+
"""
|
118
|
+
res = await self._http.put(
|
119
|
+
f"{self._url}/Users/{user_id}", json=construct_payload(ReplaceUserInput, user)
|
120
|
+
)
|
121
|
+
user = User.model_validate(res)
|
122
|
+
logger.info(f"Replaced user with ID '{user_id}' with '{user.user_name}'.")
|
123
|
+
return user
|
124
|
+
|
125
|
+
async def update_user(
|
126
|
+
self, user_id: str, operations: list[Operation] | list[dict[str, Any]]
|
127
|
+
) -> User:
|
128
|
+
"""
|
129
|
+
Update an existing user with a list of operations. This allows you to update only specific
|
130
|
+
fields of the user without replacing the entire user.
|
131
|
+
:param user_id: The ID of the user to update.
|
132
|
+
:param operations: A list of operations to perform on the user. Each operation can either be
|
133
|
+
an Operation object or a dict. If you pass a dict, it will be validated against
|
134
|
+
the Operation model before sending. If the operation is invalid, this will raise a
|
135
|
+
pydantic.ValidationError. You can also use the models Replace, Add and Remove which
|
136
|
+
are subclasses of Operation and provide a more convenient way to create operations.
|
137
|
+
:return: The updated User object.
|
138
|
+
"""
|
139
|
+
res = await self._http.patch(
|
140
|
+
f"{self._url}/Users/{user_id}",
|
141
|
+
json={
|
142
|
+
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
|
143
|
+
"Operations": [construct_payload(Operation, e) for e in operations],
|
144
|
+
},
|
145
|
+
)
|
146
|
+
user = User.model_validate(res)
|
147
|
+
logger.info(f"Updated user with ID '{user_id}'.")
|
148
|
+
return user
|
@@ -3,8 +3,8 @@ from asyncio import gather
|
|
3
3
|
from itertools import chain
|
4
4
|
from typing import Any, Literal, overload
|
5
5
|
|
6
|
-
from anaplan_sdk._services import
|
7
|
-
|
6
|
+
from anaplan_sdk._services import _AsyncHttpService
|
7
|
+
from anaplan_sdk._utils import (
|
8
8
|
parse_calendar_response,
|
9
9
|
parse_insertion_response,
|
10
10
|
sort_params,
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from ._alm import _AlmClient
|
2
|
+
from ._audit import _AuditClient
|
3
|
+
from ._bulk import Client
|
4
|
+
from ._cloud_works import _CloudWorksClient
|
5
|
+
from ._cw_flow import _FlowClient
|
6
|
+
from ._scim import _ScimClient
|
7
|
+
from ._transactional import _TransactionalClient
|
8
|
+
|
9
|
+
__all__ = [
|
10
|
+
"Client",
|
11
|
+
"_AlmClient",
|
12
|
+
"_AuditClient",
|
13
|
+
"_CloudWorksClient",
|
14
|
+
"_FlowClient",
|
15
|
+
"_ScimClient",
|
16
|
+
"_TransactionalClient",
|
17
|
+
]
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import logging
|
2
2
|
from typing import Literal, overload
|
3
3
|
|
4
|
-
from anaplan_sdk._services import _HttpService
|
4
|
+
from anaplan_sdk._services import _HttpService
|
5
|
+
from anaplan_sdk._utils import sort_params
|
5
6
|
from anaplan_sdk.exceptions import AnaplanActionError
|
6
7
|
from anaplan_sdk.models import (
|
7
8
|
ModelRevision,
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from typing import Any, Literal
|
2
2
|
|
3
|
-
from anaplan_sdk._services import _HttpService
|
3
|
+
from anaplan_sdk._services import _HttpService
|
4
|
+
from anaplan_sdk._utils import sort_params
|
4
5
|
from anaplan_sdk.models import User
|
5
6
|
|
6
7
|
Event = Literal["all", "byok", "user_activity"]
|
@@ -8,7 +8,8 @@ import httpx
|
|
8
8
|
from typing_extensions import Self
|
9
9
|
|
10
10
|
from anaplan_sdk._auth import _create_auth
|
11
|
-
from anaplan_sdk._services import _HttpService
|
11
|
+
from anaplan_sdk._services import _HttpService
|
12
|
+
from anaplan_sdk._utils import action_url, models_url, sort_params
|
12
13
|
from anaplan_sdk.exceptions import AnaplanActionError, InvalidIdentifierException
|
13
14
|
from anaplan_sdk.models import (
|
14
15
|
Action,
|
@@ -26,6 +27,7 @@ from anaplan_sdk.models import (
|
|
26
27
|
from ._alm import _AlmClient
|
27
28
|
from ._audit import _AuditClient
|
28
29
|
from ._cloud_works import _CloudWorksClient
|
30
|
+
from ._scim import _ScimClient
|
29
31
|
from ._transactional import _TransactionalClient
|
30
32
|
|
31
33
|
SortBy = Literal["id", "name"] | None
|
@@ -138,9 +140,10 @@ class Client:
|
|
138
140
|
_TransactionalClient(self._http, model_id) if model_id else None
|
139
141
|
)
|
140
142
|
self._alm_client = _AlmClient(self._http, model_id) if model_id else None
|
143
|
+
self._audit_client = _AuditClient(self._http)
|
144
|
+
self._scim_client = _ScimClient(self._http)
|
141
145
|
self._cloud_works = _CloudWorksClient(self._http)
|
142
146
|
self._thread_count = multiprocessing.cpu_count()
|
143
|
-
self._audit = _AuditClient(self._http)
|
144
147
|
self.status_poll_delay = status_poll_delay
|
145
148
|
self.upload_parallel = upload_parallel
|
146
149
|
self.upload_chunk_size = upload_chunk_size
|
@@ -173,7 +176,7 @@ class Client:
|
|
173
176
|
The Audit Client provides access to the Anaplan Audit API.
|
174
177
|
For details, see https://vinzenzklass.github.io/anaplan-sdk/guides/audit/.
|
175
178
|
"""
|
176
|
-
return self.
|
179
|
+
return self._audit_client
|
177
180
|
|
178
181
|
@property
|
179
182
|
def cw(self) -> _CloudWorksClient:
|
@@ -222,6 +225,15 @@ class Client:
|
|
222
225
|
)
|
223
226
|
return self._alm_client
|
224
227
|
|
228
|
+
@property
|
229
|
+
def scim(self) -> _ScimClient:
|
230
|
+
"""
|
231
|
+
To use the SCIM API, you must be User Admin. The SCIM API allows managing internal users.
|
232
|
+
Visiting users are excluded from the SCIM API.
|
233
|
+
:return: The SCIM Client.
|
234
|
+
"""
|
235
|
+
return self._scim_client
|
236
|
+
|
225
237
|
def get_workspaces(
|
226
238
|
self,
|
227
239
|
search_pattern: str | None = None,
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import logging
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
-
from anaplan_sdk._services import _HttpService
|
4
|
+
from anaplan_sdk._services import _HttpService
|
5
|
+
from anaplan_sdk._utils import construct_payload
|
5
6
|
from anaplan_sdk.models.flows import Flow, FlowInput, FlowSummary
|
6
7
|
|
7
8
|
logger = logging.getLogger("anaplan_sdk")
|
@@ -0,0 +1,145 @@
|
|
1
|
+
import logging
|
2
|
+
from concurrent.futures import ThreadPoolExecutor
|
3
|
+
from itertools import chain
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from anaplan_sdk._services import _HttpService
|
7
|
+
from anaplan_sdk._utils import construct_payload
|
8
|
+
from anaplan_sdk.models.scim import (
|
9
|
+
Operation,
|
10
|
+
ReplaceUserInput,
|
11
|
+
Resource,
|
12
|
+
Schema,
|
13
|
+
ServiceProviderConfig,
|
14
|
+
User,
|
15
|
+
UserInput,
|
16
|
+
field,
|
17
|
+
)
|
18
|
+
|
19
|
+
logger = logging.getLogger("anaplan_sdk")
|
20
|
+
|
21
|
+
|
22
|
+
class _ScimClient:
|
23
|
+
def __init__(self, http: _HttpService) -> None:
|
24
|
+
self._http = http
|
25
|
+
self._url = "https://api.anaplan.com/scim/1/0/v2"
|
26
|
+
|
27
|
+
def get_service_provider_config(self) -> ServiceProviderConfig:
|
28
|
+
"""
|
29
|
+
Get the SCIM Service Provider Configuration.
|
30
|
+
:return: The ServiceProviderConfig object describing the available SCIM features.
|
31
|
+
"""
|
32
|
+
res = self._http.get(f"{self._url}/ServiceProviderConfig")
|
33
|
+
return ServiceProviderConfig.model_validate(res)
|
34
|
+
|
35
|
+
def get_resource_types(self) -> list[Resource]:
|
36
|
+
"""
|
37
|
+
Get the SCIM Resource Types.
|
38
|
+
:return: A list of Resource objects describing the SCIM resource types.
|
39
|
+
"""
|
40
|
+
res = self._http.get(f"{self._url}/ResourceTypes")
|
41
|
+
return [Resource.model_validate(e) for e in res.get("Resources", [])]
|
42
|
+
|
43
|
+
def get_resource_schemas(self) -> list[Schema]:
|
44
|
+
"""
|
45
|
+
Get the SCIM Resource Schemas.
|
46
|
+
:return: A list of Schema objects describing the SCIM resource schemas.
|
47
|
+
"""
|
48
|
+
res = self._http.get(f"{self._url}/Schemas")
|
49
|
+
return [Schema.model_validate(e) for e in res.get("Resources", [])]
|
50
|
+
|
51
|
+
def get_users(self, predicate: str | field = None, page_size: int = 100) -> list[User]:
|
52
|
+
"""
|
53
|
+
Get a list of users, optionally filtered by a predicate. Keep in mind that this will only
|
54
|
+
return internal users. To get a list of all users in the tenant, use the `get_users()`
|
55
|
+
in the `audit` namespace instead.
|
56
|
+
:param predicate: A filter predicate to filter the users. This can either be a string,
|
57
|
+
in which case it will be passed as-is, or an expression. Anaplan supports filtering
|
58
|
+
on the following fields: "id", "externalId", "userName", "name.familyName",
|
59
|
+
"name.givenName" and "active". It supports the operators "eq", "ne", "gt", "ge",
|
60
|
+
"lt", "le" and "pr". It supports logical operators "and" and "or", "not" is not
|
61
|
+
supported. It supports grouping with parentheses.
|
62
|
+
:param page_size: The number of users to fetch per page. Values above 100 will error.
|
63
|
+
:return: The internal users optionally matching the filter.
|
64
|
+
"""
|
65
|
+
params: dict[str, int | str] = {"startIndex": 1, "count": page_size}
|
66
|
+
if predicate is not None:
|
67
|
+
_predicate = predicate if isinstance(predicate, str) else str(predicate)
|
68
|
+
logger.debug(f"Searching for users with predicate: {_predicate}")
|
69
|
+
params["filter"] = _predicate
|
70
|
+
res = self._http.get(f"{self._url}/Users", params=params)
|
71
|
+
users = [User.model_validate(e) for e in res.get("Resources", [])]
|
72
|
+
if (total := res["totalResults"]) <= page_size:
|
73
|
+
return users
|
74
|
+
with ThreadPoolExecutor() as executor:
|
75
|
+
pages = executor.map(
|
76
|
+
lambda i: self._http.get(
|
77
|
+
f"{self._url}/Users", params=(params | {"startIndex": i, "count": page_size})
|
78
|
+
),
|
79
|
+
range(page_size + 1, total + 1, page_size),
|
80
|
+
)
|
81
|
+
for user in chain(*(p.get("Resources", []) for p in pages)):
|
82
|
+
users.append(User.model_validate(user))
|
83
|
+
return users
|
84
|
+
|
85
|
+
def get_user(self, user_id: str) -> User:
|
86
|
+
"""
|
87
|
+
Get a user by their ID.
|
88
|
+
:param user_id: The ID of the user to fetch.
|
89
|
+
:return: The User object.
|
90
|
+
"""
|
91
|
+
res = self._http.get(f"{self._url}/Users/{user_id}")
|
92
|
+
return User.model_validate(res)
|
93
|
+
|
94
|
+
def add_user(self, user: UserInput | dict[str, Any]) -> User:
|
95
|
+
"""
|
96
|
+
Add a new user to your Anaplan tenant.
|
97
|
+
:param user: The user info to add. Can either be a UserInput object or a dict. If you pass
|
98
|
+
a dict, it will be validated against the UserInput model before sending. If the info
|
99
|
+
you provided is invalid or incomplete, this will raise a pydantic.ValidationError.
|
100
|
+
:return: The created User object.
|
101
|
+
"""
|
102
|
+
res = self._http.post(f"{self._url}/Users", json=construct_payload(UserInput, user))
|
103
|
+
user = User.model_validate(res)
|
104
|
+
logger.info(f"Added user '{user.user_name}' with ID '{user.id}'.")
|
105
|
+
return user
|
106
|
+
|
107
|
+
def replace_user(self, user_id: str, user: ReplaceUserInput | dict[str, Any]):
|
108
|
+
"""
|
109
|
+
Replace an existing user with new information. Note that this will replace all fields of the
|
110
|
+
:param user_id: ID of the user to replace.
|
111
|
+
:param user: The new user info. Can either be a ReplaceUserInput object or a dict. If you
|
112
|
+
pass a dict, it will be validated against the ReplaceUserInput model before sending.
|
113
|
+
If the info you provided is invalid or incomplete, this will raise a
|
114
|
+
pydantic.ValidationError.
|
115
|
+
:return: The updated User object.
|
116
|
+
"""
|
117
|
+
res = self._http.put(
|
118
|
+
f"{self._url}/Users/{user_id}", json=construct_payload(ReplaceUserInput, user)
|
119
|
+
)
|
120
|
+
user = User.model_validate(res)
|
121
|
+
logger.info(f"Replaced user with ID '{user_id}' with '{user.user_name}'.")
|
122
|
+
return user
|
123
|
+
|
124
|
+
def update_user(self, user_id: str, operations: list[Operation] | list[dict[str, Any]]) -> User:
|
125
|
+
"""
|
126
|
+
Update an existing user with a list of operations. This allows you to update only specific
|
127
|
+
fields of the user without replacing the entire user.
|
128
|
+
:param user_id: The ID of the user to update.
|
129
|
+
:param operations: A list of operations to perform on the user. Each operation can either be
|
130
|
+
an Operation object or a dict. If you pass a dict, it will be validated against
|
131
|
+
the Operation model before sending. If the operation is invalid, this will raise a
|
132
|
+
pydantic.ValidationError. You can also use the models Replace, Add and Remove which
|
133
|
+
are subclasses of Operation and provide a more convenient way to create operations.
|
134
|
+
:return: The updated User object.
|
135
|
+
"""
|
136
|
+
res = self._http.patch(
|
137
|
+
f"{self._url}/Users/{user_id}",
|
138
|
+
json={
|
139
|
+
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
|
140
|
+
"Operations": [construct_payload(Operation, e) for e in operations],
|
141
|
+
},
|
142
|
+
)
|
143
|
+
user = User.model_validate(res)
|
144
|
+
logger.info(f"Updated user with ID '{user_id}'.")
|
145
|
+
return user
|
@@ -3,8 +3,8 @@ from concurrent.futures import ThreadPoolExecutor
|
|
3
3
|
from itertools import chain
|
4
4
|
from typing import Any, Literal, overload
|
5
5
|
|
6
|
-
from anaplan_sdk._services import
|
7
|
-
|
6
|
+
from anaplan_sdk._services import _HttpService
|
7
|
+
from anaplan_sdk._utils import (
|
8
8
|
parse_calendar_response,
|
9
9
|
parse_insertion_response,
|
10
10
|
sort_params,
|