anaplan-sdk 0.4.2__tar.gz → 0.4.3a1__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.4.2 → anaplan_sdk-0.4.3a1}/.gitignore +1 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/PKG-INFO +1 -1
- anaplan_sdk-0.4.3a1/anaplan_sdk/__init__.py +5 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_async_clients/_bulk.py +6 -2
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_auth.py +32 -2
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_clients/_bulk.py +6 -2
- anaplan_sdk-0.4.3a1/anaplan_sdk/_oauth.py +198 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/api/async/async_alm_client.md +6 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/api/async/async_audit_client.md +6 -0
- anaplan_sdk-0.4.3a1/docs/api/async/async_client.md +9 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/api/async/async_cw_client.md +6 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/api/async/async_flows_client.md +6 -0
- anaplan_sdk-0.4.3a1/docs/api/async/async_oauth_client.md +9 -0
- anaplan_sdk-0.4.3a1/docs/api/async/async_transactional_client.md +11 -0
- anaplan_sdk-0.4.3a1/docs/api/exceptions.md +9 -0
- anaplan_sdk-0.4.3a1/docs/api/models/alm.md +7 -0
- anaplan_sdk-0.4.3a1/docs/api/models/bulk.md +7 -0
- anaplan_sdk-0.4.3a1/docs/api/models/cloud_works.md +7 -0
- anaplan_sdk-0.4.3a1/docs/api/models/flows.md +7 -0
- anaplan_sdk-0.4.3a1/docs/api/models/transactional.md +7 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/api/sync/sync_alm_client.md +6 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/api/sync/sync_audit_client.md +6 -0
- anaplan_sdk-0.4.3a1/docs/api/sync/sync_client.md +9 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/api/sync/sync_cw_client.md +6 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/api/sync/sync_flows_client.md +6 -0
- anaplan_sdk-0.4.3a1/docs/api/sync/sync_oauth_client.md +10 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/api/sync/sync_transactional_client.md +6 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/css/styles.css +3 -3
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/guides/authentication.md +84 -3
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/guides/index.md +1 -1
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/quickstart.md +3 -3
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/mkdocs.yml +10 -2
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/pyproject.toml +101 -102
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/uv.lock +1162 -1180
- anaplan_sdk-0.4.2/anaplan_sdk/__init__.py +0 -4
- anaplan_sdk-0.4.2/docs/api/async/async_client.md +0 -1
- anaplan_sdk-0.4.2/docs/api/async/async_transactional_client.md +0 -5
- anaplan_sdk-0.4.2/docs/api/exceptions.md +0 -4
- anaplan_sdk-0.4.2/docs/api/models.md +0 -4
- anaplan_sdk-0.4.2/docs/api/sync/sync_client.md +0 -1
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/.github/dependabot.yml +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/.github/workflows/docs.yml +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/.github/workflows/lint.yml +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/.github/workflows/tests.yml +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/.pre-commit-config.yaml +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/LICENSE +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/README.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_async_clients/__init__.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_async_clients/_alm.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_async_clients/_audit.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_async_clients/_cloud_works.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_async_clients/_cw_flow.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_async_clients/_transactional.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_base.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_clients/__init__.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_clients/_alm.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_clients/_audit.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_clients/_cloud_works.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_clients/_cw_flow.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/_clients/_transactional.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/exceptions.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/models/__init__.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/models/_alm.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/models/_base.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/models/_bulk.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/models/_transactional.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/models/cloud_works.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/anaplan_sdk/models/flows.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/anaplan_explained.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/assets/overview.html +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/guides/alm.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/guides/audit.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/guides/bulk.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/guides/bulk_vs_transactional.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/guides/cloud_works.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/guides/logging.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/guides/multiple_models.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/guides/transactional.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/img/anaplan-sdk.webp +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/index.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/installation.md +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/js/assets/hljs.js +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/js/assets/hljs.min.js +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/js/assets/python.js +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/js/assets/python.min.js +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/js/highlight.js +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/docs/js/highlight.min.js +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/async/conftest.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/async/test_async_alm_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/async/test_async_audit_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/async/test_async_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/async/test_async_cloud_works_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/async/test_async_flows_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/async/test_async_transactional_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/conftest.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/sync/conftest.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/sync/test_alm_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/sync/test_audit_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/sync/test_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/sync/test_cloud_works_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/tests/sync/test_flows_client.py +0 -0
- {anaplan_sdk-0.4.2 → anaplan_sdk-0.4.3a1}/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.4.
|
3
|
+
Version: 0.4.3a1
|
4
4
|
Summary: Streamlined Python Interface for Anaplan
|
5
5
|
Project-URL: Homepage, https://vinzenzklass.github.io/anaplan-sdk/
|
6
6
|
Project-URL: Repository, https://github.com/VinzenzKlass/anaplan-sdk
|
@@ -6,7 +6,7 @@ from typing import AsyncIterator, Iterator
|
|
6
6
|
import httpx
|
7
7
|
from typing_extensions import Self
|
8
8
|
|
9
|
-
from anaplan_sdk._auth import AuthCodeCallback, AuthTokenRefreshCallback,
|
9
|
+
from anaplan_sdk._auth import AuthCodeCallback, AuthTokenRefreshCallback, _create_auth
|
10
10
|
from anaplan_sdk._base import _AsyncBaseClient, action_url
|
11
11
|
from anaplan_sdk.exceptions import AnaplanActionError, InvalidIdentifierException
|
12
12
|
from anaplan_sdk.models import (
|
@@ -58,6 +58,8 @@ class AsyncClient(_AsyncBaseClient):
|
|
58
58
|
oauth2_scope: str = "openid profile email offline_access",
|
59
59
|
on_auth_code: AuthCodeCallback = None,
|
60
60
|
on_token_refresh: AuthTokenRefreshCallback = None,
|
61
|
+
oauth_token: dict[str, str] | None = None,
|
62
|
+
token: str | None = None,
|
61
63
|
timeout: float | httpx.Timeout = 30,
|
62
64
|
retry_count: int = 2,
|
63
65
|
status_poll_delay: int = 1,
|
@@ -130,7 +132,9 @@ class AsyncClient(_AsyncBaseClient):
|
|
130
132
|
"""
|
131
133
|
_client = httpx.AsyncClient(
|
132
134
|
auth=(
|
133
|
-
|
135
|
+
_create_auth(
|
136
|
+
token=token,
|
137
|
+
oauth_token=oauth_token,
|
134
138
|
user_email=user_email,
|
135
139
|
password=password,
|
136
140
|
certificate=certificate,
|
@@ -50,6 +50,18 @@ class _AnaplanAuth(httpx.Auth):
|
|
50
50
|
self._token: str = response.json()["tokenInfo"]["tokenValue"]
|
51
51
|
|
52
52
|
|
53
|
+
class StaticTokenAuth(httpx.Auth):
|
54
|
+
def __init__(self, token: str):
|
55
|
+
logger.warning("Using static token authentication. Tokens will not be refreshed.")
|
56
|
+
self._token = token
|
57
|
+
|
58
|
+
def auth_flow(self, request):
|
59
|
+
request.headers["Authorization"] = f"AnaplanAuthToken {self._token}"
|
60
|
+
response = yield request
|
61
|
+
if response.status_code == 401:
|
62
|
+
raise InvalidCredentialsException("Your token is invalid or expired.")
|
63
|
+
|
64
|
+
|
53
65
|
class AnaplanBasicAuth(_AnaplanAuth):
|
54
66
|
def __init__(self, user_email: str, password: str):
|
55
67
|
self.user_email = user_email
|
@@ -229,7 +241,7 @@ class AnaplanOauth2AuthCodeAuth(_AnaplanAuth):
|
|
229
241
|
raise InvalidCredentialsException("Error during OAuth2 authorization flow.") from error
|
230
242
|
|
231
243
|
|
232
|
-
def
|
244
|
+
def _create_auth(
|
233
245
|
user_email: str | None = None,
|
234
246
|
password: str | None = None,
|
235
247
|
certificate: str | bytes | None = None,
|
@@ -242,7 +254,25 @@ def create_auth(
|
|
242
254
|
oauth2_scope: str = "openid profile email offline_access",
|
243
255
|
on_auth_code: AuthCodeCallback = None,
|
244
256
|
on_token_refresh: AuthTokenRefreshCallback = None,
|
245
|
-
|
257
|
+
token: str | None = None,
|
258
|
+
oauth_token: dict[str, str] | None = None,
|
259
|
+
) -> httpx.Auth:
|
260
|
+
if token:
|
261
|
+
# TODO: If other parameters are provided that allow refreshing the token, use them to create
|
262
|
+
# use them to create one of the other auth classes instead.
|
263
|
+
return StaticTokenAuth(token)
|
264
|
+
if oauth_token:
|
265
|
+
if not isinstance(oauth_token, dict) and all(
|
266
|
+
f in oauth_token for f in ("access_token", "refresh_token", "token_type", "scope")
|
267
|
+
):
|
268
|
+
raise ValueError(
|
269
|
+
"oauth_token must be a dictionary with at least 'access_token', 'refresh_token', "
|
270
|
+
"'token_type', and 'scope' keys."
|
271
|
+
)
|
272
|
+
# TODO: If client_id, client_secret, and redirect_uri are provided, use them to create an
|
273
|
+
# AnaplanOauth2AuthCodeAuth (extend to accept existing `access_token` instance. Else, use
|
274
|
+
# the StaticTokenAuth directly.
|
275
|
+
return StaticTokenAuth(oauth_token["access_token"])
|
246
276
|
if certificate and private_key:
|
247
277
|
return AnaplanCertAuth(certificate, private_key, private_key_password)
|
248
278
|
if user_email and password:
|
@@ -8,7 +8,7 @@ from typing import Callable, Iterator
|
|
8
8
|
import httpx
|
9
9
|
from typing_extensions import Self
|
10
10
|
|
11
|
-
from anaplan_sdk._auth import
|
11
|
+
from anaplan_sdk._auth import _create_auth
|
12
12
|
from anaplan_sdk._base import _BaseClient, action_url
|
13
13
|
from anaplan_sdk.exceptions import AnaplanActionError, InvalidIdentifierException
|
14
14
|
from anaplan_sdk.models import (
|
@@ -60,6 +60,8 @@ class Client(_BaseClient):
|
|
60
60
|
oauth2_scope: str = "openid profile email offline_access",
|
61
61
|
on_auth_code: Callable[[str], str] | None = None,
|
62
62
|
on_token_refresh: Callable[[dict[str, str]], None] | None = None,
|
63
|
+
oauth_token: dict[str, str] | None = None,
|
64
|
+
token: str | None = None,
|
63
65
|
timeout: float | httpx.Timeout = 30,
|
64
66
|
retry_count: int = 2,
|
65
67
|
status_poll_delay: int = 1,
|
@@ -124,7 +126,9 @@ class Client(_BaseClient):
|
|
124
126
|
"""
|
125
127
|
_client = httpx.Client(
|
126
128
|
auth=(
|
127
|
-
|
129
|
+
_create_auth(
|
130
|
+
token=token,
|
131
|
+
oauth_token=oauth_token,
|
128
132
|
user_email=user_email,
|
129
133
|
password=password,
|
130
134
|
certificate=certificate,
|
@@ -0,0 +1,198 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import Callable
|
3
|
+
|
4
|
+
import httpx
|
5
|
+
|
6
|
+
from .exceptions import AnaplanException, InvalidCredentialsException
|
7
|
+
|
8
|
+
logger = logging.getLogger("anaplan_sdk")
|
9
|
+
|
10
|
+
|
11
|
+
class _BaseOauth:
|
12
|
+
def __init__(
|
13
|
+
self,
|
14
|
+
client_id: str,
|
15
|
+
client_secret: str,
|
16
|
+
redirect_url: str,
|
17
|
+
authorization_url: str = "https://us1a.app.anaplan.com/auth/prelogin",
|
18
|
+
token_url: str = "https://us1a.app.anaplan.com/oauth/token",
|
19
|
+
validation_url: str = "https://auth.anaplan.com/token/validate",
|
20
|
+
scope: str = "openid profile email offline_access",
|
21
|
+
state_generator: Callable[[], str] | None = None,
|
22
|
+
):
|
23
|
+
"""
|
24
|
+
Initializes the OAuth Client. This class provides the two utilities needed to implement
|
25
|
+
the OAuth 2.0 authorization code flow for user-facing Web Applications. It differs from the
|
26
|
+
other Authentication Strategies in this SDK in two main ways:
|
27
|
+
|
28
|
+
1. You must implement the actual authentication flow in your application. You cannot pass
|
29
|
+
the credentials directly to the `Client` or `AsyncClient`, and this class does not
|
30
|
+
implement the SDK internal authentication flow, i.e. it does not subclass `httpx.Auth`.
|
31
|
+
|
32
|
+
2. You then simply pass the resulting token to the `Client` or `AsyncClient`, rather than
|
33
|
+
passing the credentials directly, which will internally construct an `httpx.Auth` instance
|
34
|
+
|
35
|
+
Note that this class exist for convenience only, and you can implement the OAuth 2.0 Flow
|
36
|
+
yourself in your preferred library, or bring an existing implementation. For details on the
|
37
|
+
Anaplan OAuth 2.0 Flow, see the [the Docs](https://anaplanoauth2service.docs.apiary.io/#reference/overview-of-the-authorization-code-grant).
|
38
|
+
:param client_id: The client ID of your Anaplan Oauth 2.0 application. This Application
|
39
|
+
must be an Authorization Code Grant application.
|
40
|
+
:param client_secret: The client secret of your Anaplan Oauth 2.0 application.
|
41
|
+
:param redirect_url: The URL to which the user will be redirected after authorizing the
|
42
|
+
application.
|
43
|
+
:param authorization_url: The URL to which the user will be redirected to authorize the
|
44
|
+
application. Defaults to the Anaplan Prelogin Page, where the user can select the
|
45
|
+
login method.
|
46
|
+
:param token_url: The URL to post the authorization code to in order to fetch the access
|
47
|
+
token.
|
48
|
+
:param validation_url: The URL to validate the access token.
|
49
|
+
:param scope: The scope of the access request.
|
50
|
+
:param state_generator: A callable that generates a random state string. You can optionally
|
51
|
+
pass this if you need to customize the state generation logic. If not provided,
|
52
|
+
the state will be generated by `oauthlib`.
|
53
|
+
"""
|
54
|
+
self._client_id = client_id
|
55
|
+
self._client_secret = client_secret
|
56
|
+
self._redirect_url = redirect_url
|
57
|
+
self._authorization_url = authorization_url
|
58
|
+
self._token_url = token_url
|
59
|
+
self._validation_url = validation_url
|
60
|
+
self._scope = scope
|
61
|
+
|
62
|
+
try:
|
63
|
+
from oauthlib.oauth2 import WebApplicationClient
|
64
|
+
except ImportError as e:
|
65
|
+
raise AnaplanException(
|
66
|
+
"oauthlib is not available. Please install anaplan-sdk with the oauth extra "
|
67
|
+
"`pip install anaplan-sdk[oauth]` or install oauthlib separately."
|
68
|
+
) from e
|
69
|
+
self._oauth = WebApplicationClient(client_id=client_id, client_secret=client_secret)
|
70
|
+
self._state_generator = state_generator if state_generator else self._oauth.state_generator
|
71
|
+
|
72
|
+
def authorization_url(
|
73
|
+
self, authorization_url: str | None = None, state: str | None = None
|
74
|
+
) -> tuple[str, str]:
|
75
|
+
"""
|
76
|
+
Generates the authorization URL for the OAuth 2.0 flow.
|
77
|
+
:param authorization_url: You can optionally pass a custom authorization URL. This is
|
78
|
+
useful if you want to redirect i.e. redirect the user directly to the Anaplan login
|
79
|
+
page rather than the Prelogin page in only one scenario, while still reusing the
|
80
|
+
Client.
|
81
|
+
:param state: You can optionally pass a custom state string. If not provided, a random
|
82
|
+
state string will be generated by the `oauthlib` library, or by the
|
83
|
+
`state_generator` callable if provided.
|
84
|
+
:return: A tuple containing the authorization URL and the state string.
|
85
|
+
"""
|
86
|
+
auth_url = authorization_url or self._authorization_url
|
87
|
+
state = state or self._state_generator()
|
88
|
+
url, _, _ = self._oauth.prepare_authorization_request(
|
89
|
+
auth_url, state, self._redirect_url, self._scope
|
90
|
+
)
|
91
|
+
return url, state
|
92
|
+
|
93
|
+
|
94
|
+
class AsyncOauth(_BaseOauth):
|
95
|
+
"""
|
96
|
+
Asynchronous Variant of the Anaplan OAuth client for interactive OAuth Flows in Web
|
97
|
+
Applications.
|
98
|
+
"""
|
99
|
+
|
100
|
+
async def fetch_token(self, authorization_response: str) -> dict[str, str]:
|
101
|
+
"""
|
102
|
+
Fetches the token using the authorization response from the OAuth 2.0 flow.
|
103
|
+
:param authorization_response: The full URL that the user was redirected to after
|
104
|
+
authorizing the application. This URL will contain the authorization code and state.
|
105
|
+
:return: The token as a dictionary containing the access token, refresh token, scope,
|
106
|
+
expires_in, and type.
|
107
|
+
"""
|
108
|
+
from oauthlib.oauth2 import OAuth2Error
|
109
|
+
|
110
|
+
try:
|
111
|
+
url, headers, body = self._oauth.prepare_token_request(
|
112
|
+
authorization_response=authorization_response,
|
113
|
+
token_url=self._token_url,
|
114
|
+
redirect_url=self._redirect_url,
|
115
|
+
client_secret=self._client_secret,
|
116
|
+
)
|
117
|
+
async with httpx.AsyncClient() as client:
|
118
|
+
response = await client.post(url=url, headers=headers, content=body)
|
119
|
+
if not response.is_success:
|
120
|
+
raise InvalidCredentialsException(
|
121
|
+
f"Token request failed: {response.status_code} {response.text}"
|
122
|
+
)
|
123
|
+
return response.json()
|
124
|
+
except (httpx.HTTPError, ValueError, TypeError, OAuth2Error) as error:
|
125
|
+
logger.error(error)
|
126
|
+
raise AnaplanException("Error during token creation.") from error
|
127
|
+
|
128
|
+
async def validate_token(self, token: str) -> dict[str, str | dict[str, str]]:
|
129
|
+
"""
|
130
|
+
Validates the provided token by checking its validity with the Anaplan Authentication API.
|
131
|
+
If the token is not valid, an `InvalidCredentialsException` is raised.
|
132
|
+
:param token: The access token to validate.
|
133
|
+
:return: The Token information as a dictionary containing the token's details.
|
134
|
+
"""
|
135
|
+
try:
|
136
|
+
async with httpx.AsyncClient() as client:
|
137
|
+
response = await client.get(
|
138
|
+
url=self._validation_url, headers={"Authorization": f"AnaplanAuthToken {token}"}
|
139
|
+
)
|
140
|
+
if not response.is_success:
|
141
|
+
raise InvalidCredentialsException
|
142
|
+
return response.json()
|
143
|
+
except httpx.HTTPError as error:
|
144
|
+
logger.error(error)
|
145
|
+
raise AnaplanException("Error during token validation.") from error
|
146
|
+
|
147
|
+
|
148
|
+
class Oauth(_BaseOauth):
|
149
|
+
"""
|
150
|
+
Synchronous Variant of the Anaplan OAuth client for interactive OAuth Flows in Web
|
151
|
+
Applications.
|
152
|
+
"""
|
153
|
+
|
154
|
+
def fetch_token(self, authorization_response: str) -> dict[str, str]:
|
155
|
+
"""
|
156
|
+
Fetches the token using the authorization response from the OAuth 2.0 flow.
|
157
|
+
:param authorization_response: The full URL that the user was redirected to after
|
158
|
+
authorizing the application. This URL will contain the authorization code and state.
|
159
|
+
:return: The token as a dictionary containing the access token, refresh token, scope,
|
160
|
+
expires_in, and type.
|
161
|
+
"""
|
162
|
+
from oauthlib.oauth2 import OAuth2Error
|
163
|
+
|
164
|
+
try:
|
165
|
+
url, headers, body = self._oauth.prepare_token_request(
|
166
|
+
authorization_response=authorization_response,
|
167
|
+
token_url=self._token_url,
|
168
|
+
redirect_url=self._redirect_url,
|
169
|
+
client_secret=self._client_secret,
|
170
|
+
)
|
171
|
+
with httpx.Client() as client:
|
172
|
+
response = client.post(url=url, headers=headers, content=body)
|
173
|
+
if not response.is_success:
|
174
|
+
raise AnaplanException(
|
175
|
+
f"Token request failed: {response.status_code} {response.text}"
|
176
|
+
)
|
177
|
+
return response.json()
|
178
|
+
except (httpx.HTTPError, ValueError, TypeError, OAuth2Error) as error:
|
179
|
+
raise AnaplanException("Error during token creation.") from error
|
180
|
+
|
181
|
+
def validate_token(self, token: str) -> dict[str, str | dict[str, str]]:
|
182
|
+
"""
|
183
|
+
Validates the provided token by checking its validity with the Anaplan Authentication API.
|
184
|
+
If the token is not valid, an `InvalidCredentialsException` is raised.
|
185
|
+
:param token: The access token to validate.
|
186
|
+
:return: The Token information as a dictionary containing the token's details.
|
187
|
+
"""
|
188
|
+
try:
|
189
|
+
with httpx.Client() as client:
|
190
|
+
response = client.get(
|
191
|
+
url=self._validation_url, headers={"Authorization": f"AnaplanAuthToken {token}"}
|
192
|
+
)
|
193
|
+
if not response.is_success:
|
194
|
+
raise InvalidCredentialsException
|
195
|
+
return response.json()
|
196
|
+
except httpx.HTTPError as error:
|
197
|
+
logger.error(error)
|
198
|
+
raise AnaplanException("Error during token validation.") from error
|
@@ -3,3 +3,9 @@
|
|
3
3
|
instance of [AsyncClient](async_client.md). For more details, see the [Guide](../../guides/cloud_works.md).
|
4
4
|
|
5
5
|
::: anaplan_sdk._async_clients._AsyncCloudWorksClient
|
6
|
+
|
7
|
+
<style>
|
8
|
+
[data-md-component="toc"] li:first-of-type{
|
9
|
+
display: none!important;
|
10
|
+
}
|
11
|
+
</style>
|
@@ -3,3 +3,9 @@
|
|
3
3
|
instance of [AsyncClient](async_client.md). For more details, see the [Guide](../../guides/cloud_works.md).
|
4
4
|
|
5
5
|
::: anaplan_sdk._async_clients._cw_flow._AsyncFlowClient
|
6
|
+
|
7
|
+
<style>
|
8
|
+
[data-md-component="toc"] li:first-of-type{
|
9
|
+
display: none!important;
|
10
|
+
}
|
11
|
+
</style>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
???+ note
|
2
|
+
This Class is not meant to be instantiated directly, but rather accessed through the `transactional` Property on an
|
3
|
+
instance of [AsyncClient](async_client.md). For more details, see the [Guide](../../guides/transactional.md).
|
4
|
+
|
5
|
+
::: anaplan_sdk._async_clients._AsyncTransactionalClient
|
6
|
+
|
7
|
+
<style>
|
8
|
+
[data-md-component="toc"] li:first-of-type{
|
9
|
+
display: none!important;
|
10
|
+
}
|
11
|
+
</style>
|
@@ -3,3 +3,9 @@
|
|
3
3
|
instance of [Client](sync_client.md). For more details, see the [Guide](../../guides/cloud_works.md).
|
4
4
|
|
5
5
|
::: anaplan_sdk._async_clients._cw_flow._AsyncFlowClient
|
6
|
+
|
7
|
+
<style>
|
8
|
+
[data-md-component="toc"] li:first-of-type{
|
9
|
+
display: none!important;
|
10
|
+
}
|
11
|
+
</style>
|
@@ -35,7 +35,7 @@
|
|
35
35
|
color: #D55FDE
|
36
36
|
}
|
37
37
|
|
38
|
-
.hljs-addition, .hljs-attribute, .hljs-
|
38
|
+
.hljs-addition, .hljs-attribute, .hljs-string, .hljs-regexp, .hljs-string {
|
39
39
|
color: #2fb170
|
40
40
|
}
|
41
41
|
|
@@ -43,7 +43,7 @@
|
|
43
43
|
color: #abb2bf
|
44
44
|
}
|
45
45
|
|
46
|
-
.hljs-type {
|
46
|
+
.hljs-type, .hljs-meta {
|
47
47
|
color: #E4B962;
|
48
48
|
}
|
49
49
|
|
@@ -51,7 +51,7 @@
|
|
51
51
|
color: #d0864a
|
52
52
|
}
|
53
53
|
|
54
|
-
.hljs-bullet, .hljs-link, .hljs-
|
54
|
+
.hljs-bullet, .hljs-link, .hljs-selector-id, .hljs-symbol, .hljs-title, .hljs-class .hljs-title, .hljs-title.class_ {
|
55
55
|
color: #5488e8
|
56
56
|
}
|
57
57
|
|