anaplan-sdk 0.4.1__py3-none-any.whl → 0.4.3a1__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from ._async_clients import AsyncClient
2
2
  from ._clients import Client
3
+ from ._oauth import AsyncOauth, Oauth
3
4
 
4
- __all__ = ["AsyncClient", "Client", "models", "exceptions"]
5
+ __all__ = ["AsyncClient", "AsyncOauth", "Client", "Oauth", "models", "exceptions"]
@@ -1,12 +1,12 @@
1
1
  import logging
2
2
  from asyncio import gather, sleep
3
3
  from copy import copy
4
- from typing import AsyncIterator, Callable, Iterator
4
+ from typing import AsyncIterator, Iterator
5
5
 
6
6
  import httpx
7
7
  from typing_extensions import Self
8
8
 
9
- from anaplan_sdk._auth import create_auth
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 (
@@ -56,8 +56,10 @@ class AsyncClient(_AsyncBaseClient):
56
56
  redirect_uri: str | None = None,
57
57
  refresh_token: str | None = None,
58
58
  oauth2_scope: str = "openid profile email offline_access",
59
- on_auth_code: Callable[[str], str] | None = None,
60
- on_token_refresh: Callable[[dict[str, str]], None] | None = None,
59
+ on_auth_code: AuthCodeCallback = None,
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,
@@ -94,12 +96,25 @@ class AsyncClient(_AsyncBaseClient):
94
96
  :param oauth2_scope: The scope of the Oauth2 token, if you want to narrow it.
95
97
  :param on_auth_code: A callback that takes the redirect URI as a single argument and must
96
98
  return the entire response URI. This will substitute the interactive
97
- authentication code step in the terminal.
99
+ authentication code step in the terminal. The callback can be either
100
+ a synchronous function or an async coroutine function - both will be
101
+ handled appropriately regardless of the execution context (in a thread,
102
+ with or without an event loop, etc.).
103
+ **Note**: When using asynchronous callbacks in complex applications
104
+ with multiple event loops, be aware that callbacks may execute in a
105
+ separate event loop context from where they were defined, which can
106
+ make debugging challenging.
98
107
  :param on_token_refresh: A callback function that is called whenever the token is refreshed.
108
+ This includes the initial token retrieval and any subsequent calls.
99
109
  With this you can for example securely store the token in your
100
110
  application or on your server for later reuse. The function
101
111
  must accept a single argument, which is the token dictionary
102
112
  returned by the Oauth2 token endpoint and does not return anything.
113
+ This can be either a synchronous function or an async coroutine
114
+ function. **Note**: When using asynchronous callbacks in complex
115
+ applications with multiple event loops, be aware that callbacks
116
+ may execute in a separate event loop context from where they were
117
+ defined, which can make debugging challenging.
103
118
  :param timeout: The timeout in seconds for the HTTP requests. Alternatively, you can pass
104
119
  an instance of `httpx.Timeout` to set the timeout for the HTTP requests.
105
120
  :param retry_count: The number of times to retry an HTTP request if it fails. Set this to 0
@@ -117,7 +132,9 @@ class AsyncClient(_AsyncBaseClient):
117
132
  """
118
133
  _client = httpx.AsyncClient(
119
134
  auth=(
120
- create_auth(
135
+ _create_auth(
136
+ token=token,
137
+ oauth_token=oauth_token,
121
138
  user_email=user_email,
122
139
  password=password,
123
140
  certificate=certificate,
anaplan_sdk/_auth.py CHANGED
@@ -1,7 +1,11 @@
1
+ import asyncio
2
+ import inspect
1
3
  import logging
2
4
  import os
5
+ import threading
3
6
  from base64 import b64encode
4
- from typing import Callable
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from typing import Any, Awaitable, Callable, Coroutine
5
9
 
6
10
  import httpx
7
11
 
@@ -9,6 +13,11 @@ from .exceptions import AnaplanException, InvalidCredentialsException, InvalidPr
9
13
 
10
14
  logger = logging.getLogger("anaplan_sdk")
11
15
 
16
+ AuthCodeCallback = (Callable[[str], str] | Callable[[str], Awaitable[str]]) | None
17
+ AuthTokenRefreshCallback = (
18
+ Callable[[dict[str, str]], None] | Callable[[dict[str, str]], Awaitable[None]]
19
+ ) | None
20
+
12
21
 
13
22
  class _AnaplanAuth(httpx.Auth):
14
23
  requires_response_body = True
@@ -41,6 +50,18 @@ class _AnaplanAuth(httpx.Auth):
41
50
  self._token: str = response.json()["tokenInfo"]["tokenValue"]
42
51
 
43
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
+
44
65
  class AnaplanBasicAuth(_AnaplanAuth):
45
66
  def __init__(self, user_email: str, password: str):
46
67
  self.user_email = user_email
@@ -144,8 +165,8 @@ class AnaplanOauth2AuthCodeAuth(_AnaplanAuth):
144
165
  redirect_uri: str,
145
166
  refresh_token: str | None = None,
146
167
  scope: str = "openid profile email offline_access",
147
- on_auth_code: Callable[[str], str] | None = None,
148
- on_token_refresh: Callable[[dict[str, str]], None] | None = None,
168
+ on_auth_code: AuthCodeCallback = None,
169
+ on_token_refresh: AuthTokenRefreshCallback = None,
149
170
  ):
150
171
  try:
151
172
  from oauthlib.oauth2 import WebApplicationClient
@@ -188,7 +209,7 @@ class AnaplanOauth2AuthCodeAuth(_AnaplanAuth):
188
209
  self._token = token["access_token"]
189
210
  self._refresh_token = token["refresh_token"]
190
211
  if self._on_token_refresh:
191
- self._on_token_refresh(token)
212
+ _run_callback(self._on_token_refresh, token)
192
213
  self._id_token = token.get("id_token")
193
214
 
194
215
  def __auth_code_flow(self):
@@ -202,7 +223,7 @@ class AnaplanOauth2AuthCodeAuth(_AnaplanAuth):
202
223
  scope=self._scope,
203
224
  )
204
225
  authorization_response = (
205
- self._on_auth_code(url)
226
+ _run_callback(self._on_auth_code, url)
206
227
  if self._on_auth_code
207
228
  else input(
208
229
  f"Please go to {url} and authorize the app.\n"
@@ -220,7 +241,7 @@ class AnaplanOauth2AuthCodeAuth(_AnaplanAuth):
220
241
  raise InvalidCredentialsException("Error during OAuth2 authorization flow.") from error
221
242
 
222
243
 
223
- def create_auth(
244
+ def _create_auth(
224
245
  user_email: str | None = None,
225
246
  password: str | None = None,
226
247
  certificate: str | bytes | None = None,
@@ -231,9 +252,27 @@ def create_auth(
231
252
  redirect_uri: str | None = None,
232
253
  refresh_token: str | None = None,
233
254
  oauth2_scope: str = "openid profile email offline_access",
234
- on_auth_code: Callable[[str], str] | None = None,
235
- on_token_refresh: Callable[[dict[str, str]], None] | None = None,
236
- ) -> _AnaplanAuth:
255
+ on_auth_code: AuthCodeCallback = None,
256
+ on_token_refresh: AuthTokenRefreshCallback = None,
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"])
237
276
  if certificate and private_key:
238
277
  return AnaplanCertAuth(certificate, private_key, private_key_password)
239
278
  if user_email and password:
@@ -254,3 +293,32 @@ def create_auth(
254
293
  "- certificate and private_key, or\n"
255
294
  "- client_id, client_secret, and redirect_uri"
256
295
  )
296
+
297
+
298
+ def _run_callback(func, *arg, **kwargs):
299
+ if not inspect.iscoroutinefunction(func):
300
+ return func(*arg, **kwargs)
301
+ coro = func(*arg, **kwargs)
302
+ try:
303
+ loop = asyncio.get_running_loop()
304
+ except RuntimeError:
305
+ return asyncio.run(coro)
306
+
307
+ if threading.current_thread() is threading.main_thread():
308
+ if not loop.is_running():
309
+ return loop.run_until_complete(coro)
310
+ else:
311
+ with ThreadPoolExecutor() as pool:
312
+ future = pool.submit(__run_in_new_loop, coro)
313
+ return future.result(timeout=30)
314
+ else:
315
+ return asyncio.run_coroutine_threadsafe(coro, loop).result()
316
+
317
+
318
+ def __run_in_new_loop(coroutine: Coroutine[Any, Any, Any]):
319
+ new_loop = asyncio.new_event_loop()
320
+ asyncio.set_event_loop(new_loop)
321
+ try:
322
+ return new_loop.run_until_complete(coroutine)
323
+ finally:
324
+ new_loop.close()
@@ -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 create_auth
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,
@@ -99,6 +101,7 @@ class Client(_BaseClient):
99
101
  return the entire response URI. This will substitute the interactive
100
102
  authentication code step in the terminal.
101
103
  :param on_token_refresh: A callback function that is called whenever the token is refreshed.
104
+ This includes the initial token retrieval and any subsequent calls.
102
105
  With this you can for example securely store the token in your
103
106
  application or on your server for later reuse. The function
104
107
  must accept a single argument, which is the token dictionary
@@ -123,7 +126,9 @@ class Client(_BaseClient):
123
126
  """
124
127
  _client = httpx.Client(
125
128
  auth=(
126
- create_auth(
129
+ _create_auth(
130
+ token=token,
131
+ oauth_token=oauth_token,
127
132
  user_email=user_email,
128
133
  password=password,
129
134
  certificate=certificate,
anaplan_sdk/_oauth.py ADDED
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anaplan-sdk
3
- Version: 0.4.1
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
@@ -1,18 +1,19 @@
1
- anaplan_sdk/__init__.py,sha256=5fr-SZSsH6f3vkRUTDoK6xdAN31cCpe9Mwz2VNu47Uw,134
2
- anaplan_sdk/_auth.py,sha256=LjPKGoM7qqiFmr9qC4_ZnV9sTHPbvhrnf6iSq_w1tNI,10569
1
+ anaplan_sdk/__init__.py,sha256=4P-BZnoTxQdKRHOH0DUe-B4XUQKPrK45UfMDWeb1TtI,196
2
+ anaplan_sdk/_auth.py,sha256=wo0iuQm1-uZiZ081hA2Y2knc-w1RIwhea_6CEqDxPto,13276
3
3
  anaplan_sdk/_base.py,sha256=9CdLshORWsLixOyoFa3A0Bka5lhLwlZrQI5sEdBcGFI,12298
4
+ anaplan_sdk/_oauth.py,sha256=TDv4d10htjhUIbcGrvwTf4Z7dYWoeh18Wl5CiMu93fE,9914
4
5
  anaplan_sdk/exceptions.py,sha256=ALkA9fBF0NQ7dufFxV6AivjmHyuJk9DOQ9jtJV2n7f0,1809
5
6
  anaplan_sdk/_async_clients/__init__.py,sha256=pZXgMMg4S9Aj_pxQCaSiPuNG-sePVGBtNJ0133VjqW4,364
6
7
  anaplan_sdk/_async_clients/_alm.py,sha256=O1_r-O1tNDq7vXRwE2UEFE5S2bPmPh4IAQPQ8bmZfQE,3297
7
8
  anaplan_sdk/_async_clients/_audit.py,sha256=a92RY0B3bWxp2CCAWjzqKfvBjG1LJGlai0Hn5qmwgF8,2312
8
- anaplan_sdk/_async_clients/_bulk.py,sha256=YLttQd9oVHrvafXZ_J0QD8EQ3QCtmcT9YRZQ8_X71AI,25674
9
+ anaplan_sdk/_async_clients/_bulk.py,sha256=SYYqfiZ12FEETQP2lakEU3bGfy0mEenA34Nj3dwXkLc,27077
9
10
  anaplan_sdk/_async_clients/_cloud_works.py,sha256=KPX9W55SF6h8fJd4Rx-HLq6eaRA-Vo3rFu343UiiaGQ,16642
10
11
  anaplan_sdk/_async_clients/_cw_flow.py,sha256=ZTNAbKDwb59Wg3u68hbtt1kpd-LNz9K0sftT-gvYzJQ,3651
11
12
  anaplan_sdk/_async_clients/_transactional.py,sha256=Mvr7OyBPjQRpBtzkJNfRzV4aNCzUiaYmm0zQubo62Wo,8035
12
13
  anaplan_sdk/_clients/__init__.py,sha256=FsbwvZC1FHrxuRXwbPxUzbhz_lO1DpXIxEOjx6-3QuA,219
13
14
  anaplan_sdk/_clients/_alm.py,sha256=UAdQxgHfax-VquC0YtbqrRBku2Rn35tVgwJdxYFScps,3202
14
15
  anaplan_sdk/_clients/_audit.py,sha256=xQQiwWIb4QQefolPvxNwBFE-pkRzzi8fYPyewjF63lc,2181
15
- anaplan_sdk/_clients/_bulk.py,sha256=TVinTtTYnlcoTrOX0ldpW11qui5rSUXm_PE1L7JgST8,25466
16
+ anaplan_sdk/_clients/_bulk.py,sha256=RCFczu_iePjn6O_1Oj4m8lMtaSw7bwNHIVHMz_ZsYPU,25737
16
17
  anaplan_sdk/_clients/_cloud_works.py,sha256=KAMnLoeMJ2iwMXlDSbKynCE57BtkCfOgM5O8wT1kkSs,16291
17
18
  anaplan_sdk/_clients/_cw_flow.py,sha256=5IFWFT-qbyGvaSOOtaFOjHnOlyYbj4Rj3xiavfTlm8c,3527
18
19
  anaplan_sdk/_clients/_transactional.py,sha256=YUVbA54uhMloQcahwMtmZO3YooO6qQzwZN3ZRSu_z_c,7976
@@ -23,7 +24,7 @@ anaplan_sdk/models/_bulk.py,sha256=dHP3kMvsKONCZS6mHB271-wp2S4P3rM874Ita8TzABU,8
23
24
  anaplan_sdk/models/_transactional.py,sha256=_0UbVR9D5QABI29yloYrJTSgL-K0EU7PzPeJu5LdhnY,4854
24
25
  anaplan_sdk/models/cloud_works.py,sha256=nfn_LHPR-KmW7Tpvz-5qNCzmR8SYgvsVV-lx5iDlyqI,19425
25
26
  anaplan_sdk/models/flows.py,sha256=SuLgNj5-2SeE3U1i8iY8cq2IkjuUgd_3M1n2ENructk,3625
26
- anaplan_sdk-0.4.1.dist-info/METADATA,sha256=BBxu0ztPLPPxIJtueo3P3cfs1webd9Fxu60hFdHi0p8,3543
27
- anaplan_sdk-0.4.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
28
- anaplan_sdk-0.4.1.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
29
- anaplan_sdk-0.4.1.dist-info/RECORD,,
27
+ anaplan_sdk-0.4.3a1.dist-info/METADATA,sha256=mxfCoUiocn3SPGKbUbUnHukS9eyRYj12EVMZIMFjI7k,3545
28
+ anaplan_sdk-0.4.3a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
+ anaplan_sdk-0.4.3a1.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
30
+ anaplan_sdk-0.4.3a1.dist-info/RECORD,,