anaplan-sdk 0.5.0a3__py3-none-any.whl → 0.5.0a5__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.
@@ -1,7 +1,7 @@
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, sort_params
5
5
  from anaplan_sdk.exceptions import AnaplanActionError
6
6
  from anaplan_sdk.models import (
7
7
  ModelRevision,
@@ -29,13 +29,21 @@ class _AsyncAlmClient:
29
29
  logger.info(f"Changed model status to '{status}' for model {self._model_id}.")
30
30
  await self._http.put(f"{self._url}/onlineStatus", json={"status": status})
31
31
 
32
- async def get_revisions(self) -> list[Revision]:
32
+ async def get_revisions(
33
+ self,
34
+ sort_by: Literal["id", "name", "applied_on", "created_on"] | None = None,
35
+ descending: bool = False,
36
+ ) -> list[Revision]:
33
37
  """
34
38
  Use this call to return a list of revisions for a specific model.
39
+ :param sort_by: The field to sort the results by.
40
+ :param descending: If True, the results will be sorted in descending order.
35
41
  :return: A list of revisions for a specific model.
36
42
  """
37
- res = await self._http.get(f"{self._url}/alm/revisions")
38
- return [Revision.model_validate(e) for e in res.get("revisions", [])]
43
+ res = await self._http.get_paginated(
44
+ f"{self._url}/alm/revisions", "revisions", params=sort_params(sort_by, descending)
45
+ )
46
+ return [Revision.model_validate(e) for e in res]
39
47
 
40
48
  async def get_latest_revision(self) -> Revision | None:
41
49
  """
@@ -1,9 +1,10 @@
1
1
  from typing import Any, Literal
2
2
 
3
- from anaplan_sdk._services import _AsyncHttpService
3
+ from anaplan_sdk._services import _AsyncHttpService, sort_params
4
4
  from anaplan_sdk.models import User
5
5
 
6
6
  Event = Literal["all", "byok", "user_activity"]
7
+ UserSortBy = Literal["first_name", "last_name", "email", "active", "last_login_date"] | None
7
8
 
8
9
 
9
10
  class _AsyncAuditClient:
@@ -12,22 +13,31 @@ class _AsyncAuditClient:
12
13
  self._limit = 10_000
13
14
  self._url = "https://audit.anaplan.com/audit/api/1/events"
14
15
 
15
- async def get_users(self, search_pattern: str | None = None) -> list[User]:
16
+ async def get_users(
17
+ self,
18
+ search_pattern: str | None = None,
19
+ sort_by: UserSortBy = None,
20
+ descending: bool = False,
21
+ ) -> list[User]:
16
22
  """
17
23
  Lists all the Users in the authenticated users default tenant.
18
- :param search_pattern: Optionally filter for specific users. When provided,
24
+ :param search_pattern: **Caution: This is an undocumented Feature and may behave
25
+ unpredictably. It requires the Tenant Admin role. For non-admin users, it is
26
+ ignored.** Optionally filter for specific users. When provided,
19
27
  case-insensitive matches users with emails or names containing this string.
20
28
  You can use the wildcards `%` for 0-n characters, and `_` for exactly 1 character.
21
29
  When None (default), returns all users.
30
+ :param sort_by: The field to sort the results by.
31
+ :param descending: If True, the results will be sorted in descending order.
22
32
  :return: The List of Users.
23
33
  """
24
- params = {"s": search_pattern} if search_pattern else None
25
- return [
26
- User.model_validate(e)
27
- for e in await self._http.get_paginated(
28
- "https://api.anaplan.com/2/0/users", "users", params=params
29
- )
30
- ]
34
+ params = sort_params(sort_by, descending)
35
+ if search_pattern:
36
+ params["s"] = search_pattern
37
+ res = await self._http.get_paginated(
38
+ "https://api.anaplan.com/2/0/users", "users", params=params
39
+ )
40
+ return [User.model_validate(e) for e in res]
31
41
 
32
42
  async def get_user(self, user_id: str = "me") -> User:
33
43
  """
@@ -7,7 +7,7 @@ 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, action_url, sort_params
10
+ from anaplan_sdk._services import _AsyncHttpService, action_url, models_url, sort_params
11
11
  from anaplan_sdk.exceptions import AnaplanActionError, InvalidIdentifierException
12
12
  from anaplan_sdk.models import (
13
13
  Action,
@@ -27,6 +27,8 @@ from ._audit import _AsyncAuditClient
27
27
  from ._cloud_works import _AsyncCloudWorksClient
28
28
  from ._transactional import _AsyncTransactionalClient
29
29
 
30
+ SortBy = Literal["id", "name"] | None
31
+
30
32
  logger = logging.getLogger("anaplan_sdk")
31
33
 
32
34
 
@@ -140,34 +142,24 @@ class AsyncClient:
140
142
  f"Initialized AsyncClient with workspace_id={workspace_id}, model_id={model_id}"
141
143
  )
142
144
 
143
- @classmethod
144
- def from_existing(
145
- cls, existing: Self, *, workspace_id: str | None = None, model_id: str | None = None
146
- ) -> Self:
145
+ def with_model(self, model_id: str | None = None, workspace_id: str | None = None) -> Self:
147
146
  """
148
- Create a new instance of the Client from an existing instance. This is useful if you want
149
- to interact with multiple models or workspaces in the same script but share the same
150
- authentication and configuration. This creates a shallow copy of the existing client and
151
- optionally updates the relevant attributes to the new workspace and model. You can provide
152
- either a new workspace Id or a new model Id, or both. If you do not provide one of them,
153
- the existing value will be used. If you omit both, the new instance will be an identical
154
- copy of the existing instance.
155
-
156
- :param existing: The existing instance to copy.
147
+ Create a new instance of the Client with the given model and workspace Ids. **This creates
148
+ a copy of the current client. The current instance remains unchanged.**
157
149
  :param workspace_id: The workspace Id to use or None to use the existing workspace Id.
158
150
  :param model_id: The model Id to use or None to use the existing model Id.
159
151
  :return: A new instance of the Client.
160
152
  """
161
- client = copy(existing)
162
- new_ws_id = workspace_id or existing._workspace_id
163
- new_model_id = model_id or existing._model_id
153
+ client = copy(self)
154
+ new_ws_id = workspace_id or self._workspace_id
155
+ new_model_id = model_id or self._model_id
164
156
  logger.debug(
165
157
  f"Creating a new AsyncClient from existing instance "
166
158
  f"with workspace_id={new_ws_id}, model_id={new_model_id}."
167
159
  )
168
160
  client._url = f"https://api.anaplan.com/2/0/workspaces/{new_ws_id}/models/{new_model_id}"
169
- client._transactional_client = _AsyncTransactionalClient(existing._http, new_model_id)
170
- client._alm_client = _AsyncAlmClient(existing._http, new_model_id)
161
+ client._transactional_client = _AsyncTransactionalClient(self._http, new_model_id)
162
+ client._alm_client = _AsyncAlmClient(self._http, new_model_id)
171
163
  return client
172
164
 
173
165
  @property
@@ -228,12 +220,14 @@ class AsyncClient:
228
220
  async def get_workspaces(
229
221
  self,
230
222
  search_pattern: str | None = None,
231
- sort_by: Literal["size_allowance", "name"] = "name",
223
+ sort_by: Literal["size_allowance", "name"] | None = None,
232
224
  descending: bool = False,
233
225
  ) -> list[Workspace]:
234
226
  """
235
227
  Lists all the Workspaces the authenticated user has access to.
236
- :param search_pattern: Optionally filter for specific workspaces. When provided,
228
+ :param search_pattern: **Caution: This is an undocumented Feature and may behave
229
+ unpredictably. It requires the Tenant Admin role. For non-admin users, it is
230
+ ignored.** Optionally filter for specific workspaces. When provided,
237
231
  case-insensitive matches workspaces with names containing this string.
238
232
  You can use the wildcards `%` for 0-n characters, and `_` for exactly 1 character.
239
233
  When None (default), returns all users.
@@ -244,22 +238,26 @@ class AsyncClient:
244
238
  params = {"tenantDetails": "true"} | sort_params(sort_by, descending)
245
239
  if search_pattern:
246
240
  params["s"] = search_pattern
247
- return [
248
- Workspace.model_validate(e)
249
- for e in await self._http.get_paginated(
250
- "https://api.anaplan.com/2/0/workspaces", "workspaces", params=params
251
- )
252
- ]
241
+ res = await self._http.get_paginated(
242
+ "https://api.anaplan.com/2/0/workspaces", "workspaces", params=params
243
+ )
244
+ return [Workspace.model_validate(e) for e in res]
253
245
 
254
246
  async def get_models(
255
247
  self,
248
+ only_in_workspace: bool | str = False,
256
249
  search_pattern: str | None = None,
257
- sort_by: Literal["active_state", "name"] = "name",
250
+ sort_by: Literal["active_state", "name"] | None = None,
258
251
  descending: bool = False,
259
252
  ) -> list[Model]:
260
253
  """
261
254
  Lists all the Models the authenticated user has access to.
262
- :param search_pattern: Optionally filter for specific models. When provided,
255
+ :param only_in_workspace: If True, only lists models in the workspace provided when
256
+ instantiating the client. If a string is provided, only lists models in the workspace
257
+ with the given Id. If False (default), lists models in all workspaces the user
258
+ :param search_pattern: **Caution: This is an undocumented Feature and may behave
259
+ unpredictably. It requires the Tenant Admin role. For non-admin users, it is
260
+ ignored.** Optionally filter for specific models. When provided,
263
261
  case-insensitive matches model names containing this string.
264
262
  You can use the wildcards `%` for 0-n characters, and `_` for exactly 1 character.
265
263
  When None (default), returns all models.
@@ -270,12 +268,10 @@ class AsyncClient:
270
268
  params = {"modelDetails": "true"} | sort_params(sort_by, descending)
271
269
  if search_pattern:
272
270
  params["s"] = search_pattern
273
- return [
274
- Model.model_validate(e)
275
- for e in await self._http.get_paginated(
276
- "https://api.anaplan.com/2/0/models", "models", params=params
277
- )
278
- ]
271
+ res = await self._http.get_paginated(
272
+ models_url(only_in_workspace, self._workspace_id), "models", params=params
273
+ )
274
+ return [Model.model_validate(e) for e in res]
279
275
 
280
276
  async def delete_models(self, model_ids: list[str]) -> ModelDeletionResult:
281
277
  """
@@ -291,9 +287,7 @@ class AsyncClient:
291
287
  )
292
288
  return ModelDeletionResult.model_validate(res)
293
289
 
294
- async def get_files(
295
- self, sort_by: Literal["id", "name"] = "id", descending: bool = False
296
- ) -> list[File]:
290
+ async def get_files(self, sort_by: SortBy = None, descending: bool = False) -> list[File]:
297
291
  """
298
292
  Lists all the Files in the Model.
299
293
  :param sort_by: The field to sort the results by.
@@ -305,9 +299,7 @@ class AsyncClient:
305
299
  )
306
300
  return [File.model_validate(e) for e in res]
307
301
 
308
- async def get_actions(
309
- self, sort_by: Literal["id", "name"] = "id", descending: bool = False
310
- ) -> list[Action]:
302
+ async def get_actions(self, sort_by: SortBy = None, descending: bool = False) -> list[Action]:
311
303
  """
312
304
  Lists all the Actions in the Model. This will only return the Actions listed under
313
305
  `Other Actions` in Anaplan. For Imports, exports, and processes, see their respective
@@ -322,7 +314,7 @@ class AsyncClient:
322
314
  return [Action.model_validate(e) for e in res]
323
315
 
324
316
  async def get_processes(
325
- self, sort_by: Literal["id", "name"] = "id", descending: bool = False
317
+ self, sort_by: SortBy = None, descending: bool = False
326
318
  ) -> list[Process]:
327
319
  """
328
320
  Lists all the Processes in the Model.
@@ -335,9 +327,7 @@ class AsyncClient:
335
327
  )
336
328
  return [Process.model_validate(e) for e in res]
337
329
 
338
- async def get_imports(
339
- self, sort_by: Literal["id", "name"] = "id", descending: bool = False
340
- ) -> list[Import]:
330
+ async def get_imports(self, sort_by: SortBy = None, descending: bool = False) -> list[Import]:
341
331
  """
342
332
  Lists all the Imports in the Model.
343
333
  :param sort_by: The field to sort the results by.
@@ -349,9 +339,7 @@ class AsyncClient:
349
339
  )
350
340
  return [Import.model_validate(e) for e in res]
351
341
 
352
- async def get_exports(
353
- self, sort_by: Literal["id", "name"] = "id", descending: bool = False
354
- ) -> list[Export]:
342
+ async def get_exports(self, sort_by: SortBy = None, descending: bool = False) -> list[Export]:
355
343
  """
356
344
  Lists all the Exports in the Model.
357
345
  :param sort_by: The field to sort the results by.
@@ -99,14 +99,15 @@ class _AsyncCloudWorksClient:
99
99
  logger.info(f"Deleted connection '{con_id}'.")
100
100
 
101
101
  async def get_integrations(
102
- self, sort_by_name: Literal["ascending", "descending"] = "ascending"
102
+ self, sort_by: Literal["name"] | None = None, descending: bool = False
103
103
  ) -> list[Integration]:
104
104
  """
105
105
  List all integrations in CloudWorks.
106
- :param sort_by_name: Sort the integrations by name in ascending or descending order.
106
+ :param sort_by: The field to sort the results by.
107
+ :param descending: If True, the results will be sorted in descending order.
107
108
  :return: A list of integrations.
108
109
  """
109
- params = {"sortBy": "name" if sort_by_name == "ascending" else "-name"}
110
+ params = {"sortBy": f"{'-' if descending else ''}{sort_by}"} if sort_by else None
110
111
  return [
111
112
  Integration.model_validate(e)
112
113
  for e in await self._http.get_paginated(f"{self._url}", "integrations", params=params)
@@ -30,6 +30,8 @@ from anaplan_sdk.models import (
30
30
  ViewInfo,
31
31
  )
32
32
 
33
+ SortBy = Literal["id", "name"] | None
34
+
33
35
  logger = logging.getLogger("anaplan_sdk")
34
36
 
35
37
 
@@ -69,9 +71,7 @@ class _AsyncTransactionalClient:
69
71
  )
70
72
  logger.info(f"Closed model '{self._model_id}'.")
71
73
 
72
- async def get_modules(
73
- self, sort_by: Literal["id", "name"] = "id", descending: bool = False
74
- ) -> list[Module]:
74
+ async def get_modules(self, sort_by: SortBy = None, descending: bool = False) -> list[Module]:
75
75
  """
76
76
  Lists all the Modules in the Model.
77
77
  :param sort_by: The field to sort the results by.
@@ -84,7 +84,7 @@ class _AsyncTransactionalClient:
84
84
  return [Module.model_validate(e) for e in res]
85
85
 
86
86
  async def get_views(
87
- self, sort_by: Literal["id", "module_id", "name"] = "id", descending: bool = False
87
+ self, sort_by: Literal["id", "module_id", "name"] | None = None, descending: bool = False
88
88
  ) -> list[View]:
89
89
  """
90
90
  Lists all the Views in the Model. This will include all Modules and potentially other saved
@@ -120,9 +120,7 @@ class _AsyncTransactionalClient:
120
120
  )
121
121
  return [LineItem.model_validate(e) for e in res.get("items", [])]
122
122
 
123
- async def get_lists(
124
- self, sort_by: Literal["id", "name"] = "id", descending: bool = False
125
- ) -> list[List]:
123
+ async def get_lists(self, sort_by: SortBy = None, descending: bool = False) -> list[List]:
126
124
  """
127
125
  Lists all the Lists in the Model.
128
126
  :param sort_by: The field to sort the results by.
@@ -1,7 +1,7 @@
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, sort_params
5
5
  from anaplan_sdk.exceptions import AnaplanActionError
6
6
  from anaplan_sdk.models import (
7
7
  ModelRevision,
@@ -29,13 +29,21 @@ class _AlmClient:
29
29
  logger.info(f"Changed model status to '{status}' for model {self._model_id}.")
30
30
  self._http.put(f"{self._url}/onlineStatus", json={"status": status})
31
31
 
32
- def get_revisions(self) -> list[Revision]:
32
+ def get_revisions(
33
+ self,
34
+ sort_by: Literal["id", "name", "applied_on", "created_on"] | None = None,
35
+ descending: bool = False,
36
+ ) -> list[Revision]:
33
37
  """
34
38
  Use this call to return a list of revisions for a specific model.
39
+ :param sort_by: The field to sort the results by.
40
+ :param descending: If True, the results will be sorted in descending order.
35
41
  :return: A list of revisions for a specific model.
36
42
  """
37
- res = self._http.get(f"{self._url}/alm/revisions")
38
- return [Revision.model_validate(e) for e in res.get("revisions", [])]
43
+ res = self._http.get_paginated(
44
+ f"{self._url}/alm/revisions", "revisions", params=sort_params(sort_by, descending)
45
+ )
46
+ return [Revision.model_validate(e) for e in res]
39
47
 
40
48
  def get_latest_revision(self) -> Revision | None:
41
49
  """
@@ -1,9 +1,10 @@
1
1
  from typing import Any, Literal
2
2
 
3
- from anaplan_sdk._services import _HttpService
3
+ from anaplan_sdk._services import _HttpService, sort_params
4
4
  from anaplan_sdk.models import User
5
5
 
6
6
  Event = Literal["all", "byok", "user_activity"]
7
+ UserSortBy = Literal["first_name", "last_name", "email", "active", "last_login_date"] | None
7
8
 
8
9
 
9
10
  class _AuditClient:
@@ -12,20 +13,29 @@ class _AuditClient:
12
13
  self._limit = 10_000
13
14
  self._url = "https://audit.anaplan.com/audit/api/1/events"
14
15
 
15
- def get_users(self, search_pattern: str | None = None) -> list[User]:
16
+ def get_users(
17
+ self,
18
+ search_pattern: str | None = None,
19
+ sort_by: UserSortBy = None,
20
+ descending: bool = False,
21
+ ) -> list[User]:
16
22
  """
17
23
  Lists all the Users in the authenticated users default tenant.
18
- :param search_pattern: Optional filter for users. When provided, case-insensitive matches
19
- users with emails containing this string. When None (default), returns all users.
24
+ :param search_pattern: **Caution: This is an undocumented Feature and may behave
25
+ unpredictably. It requires the Tenant Admin role. For non-admin users, it is
26
+ ignored.** Optionally filter for specific users. When provided,
27
+ case-insensitive matches users with emails or names containing this string.
28
+ You can use the wildcards `%` for 0-n characters, and `_` for exactly 1 character.
29
+ When None (default), returns all users.
30
+ :param sort_by: The field to sort the results by.
31
+ :param descending: If True, the results will be sorted in descending order.
20
32
  :return: The List of Users.
21
33
  """
22
- params = {"s": search_pattern} if search_pattern else None
23
- return [
24
- User.model_validate(e)
25
- for e in self._http.get_paginated(
26
- "https://api.anaplan.com/2/0/users", "users", params=params
27
- )
28
- ]
34
+ params = sort_params(sort_by, descending)
35
+ if search_pattern:
36
+ params["s"] = search_pattern
37
+ res = self._http.get_paginated("https://api.anaplan.com/2/0/users", "users", params=params)
38
+ return [User.model_validate(e) for e in res]
29
39
 
30
40
  def get_user(self, user_id: str = "me") -> User:
31
41
  """
@@ -2,13 +2,13 @@ import logging
2
2
  import multiprocessing
3
3
  from concurrent.futures import ThreadPoolExecutor
4
4
  from copy import copy
5
- from typing import Iterator
5
+ from typing import Iterator, Literal
6
6
 
7
7
  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, action_url
11
+ from anaplan_sdk._services import _HttpService, action_url, models_url, sort_params
12
12
  from anaplan_sdk.exceptions import AnaplanActionError, InvalidIdentifierException
13
13
  from anaplan_sdk.models import (
14
14
  Action,
@@ -28,6 +28,8 @@ from ._audit import _AuditClient
28
28
  from ._cloud_works import _CloudWorksClient
29
29
  from ._transactional import _TransactionalClient
30
30
 
31
+ SortBy = Literal["id", "name"] | None
32
+
31
33
  logger = logging.getLogger("anaplan_sdk")
32
34
 
33
35
 
@@ -145,34 +147,24 @@ class Client:
145
147
  self.allow_file_creation = allow_file_creation
146
148
  logger.debug(f"Initialized Client with workspace_id={workspace_id}, model_id={model_id}")
147
149
 
148
- @classmethod
149
- def from_existing(
150
- cls, existing: Self, *, workspace_id: str | None = None, model_id: str | None = None
151
- ) -> Self:
150
+ def with_model(self, model_id: str | None = None, workspace_id: str | None = None) -> Self:
152
151
  """
153
- Create a new instance of the Client from an existing instance. This is useful if you want
154
- to interact with multiple models or workspaces in the same script but share the same
155
- authentication and configuration. This creates a shallow copy of the existing client and
156
- optionally updates the relevant attributes to the new workspace and model. You can provide
157
- either a new workspace Id or a new model Id, or both. If you do not provide one of them,
158
- the existing value will be used. If you omit both, the new instance will be an identical
159
- copy of the existing instance.
160
-
161
- :param existing: The existing instance to copy.
152
+ Create a new instance of the Client with the given model and workspace Ids. **This creates
153
+ a copy of the current client. The current instance remains unchanged.**
162
154
  :param workspace_id: The workspace Id to use or None to use the existing workspace Id.
163
155
  :param model_id: The model Id to use or None to use the existing model Id.
164
156
  :return: A new instance of the Client.
165
157
  """
166
- client = copy(existing)
167
- new_ws_id = workspace_id or existing._workspace_id
168
- new_model_id = model_id or existing._model_id
158
+ client = copy(self)
159
+ new_ws_id = workspace_id or self._workspace_id
160
+ new_model_id = model_id or self._model_id
169
161
  logger.debug(
170
162
  f"Creating a new AsyncClient from existing instance "
171
163
  f"with workspace_id={new_ws_id}, model_id={new_model_id}."
172
164
  )
173
165
  client._url = f"https://api.anaplan.com/2/0/workspaces/{new_ws_id}/models/{new_model_id}"
174
- client._transactional_client = _TransactionalClient(existing._http, new_model_id)
175
- client._alm_client = _AlmClient(existing._http, new_model_id)
166
+ client._transactional_client = _TransactionalClient(self._http, new_model_id)
167
+ client._alm_client = _AlmClient(self._http, new_model_id)
176
168
  return client
177
169
 
178
170
  @property
@@ -230,42 +222,61 @@ class Client:
230
222
  )
231
223
  return self._alm_client
232
224
 
233
- def get_workspaces(self, search_pattern: str | None = None) -> list[Workspace]:
225
+ def get_workspaces(
226
+ self,
227
+ search_pattern: str | None = None,
228
+ sort_by: Literal["size_allowance", "name"] | None = None,
229
+ descending: bool = False,
230
+ ) -> list[Workspace]:
234
231
  """
235
232
  Lists all the Workspaces the authenticated user has access to.
236
- :param search_pattern: Optional filter for workspaces. When provided, case-insensitive
237
- matches workspaces with names containing this string. When None (default),
238
- returns all workspaces.
233
+ :param search_pattern: **Caution: This is an undocumented Feature and may behave
234
+ unpredictably. It requires the Tenant Admin role. For non-admin users, it is
235
+ ignored.** Optionally filter for specific workspaces. When provided,
236
+ case-insensitive matches workspaces with names containing this string.
237
+ You can use the wildcards `%` for 0-n characters, and `_` for exactly 1 character.
238
+ When None (default), returns all users.
239
+ :param sort_by: The field to sort the results by.
240
+ :param descending: If True, the results will be sorted in descending order.
239
241
  :return: The List of Workspaces.
240
242
  """
241
- params = {"tenantDetails": "true"}
243
+ params = {"tenantDetails": "true"} | sort_params(sort_by, descending)
242
244
  if search_pattern:
243
245
  params["s"] = search_pattern
244
- return [
245
- Workspace.model_validate(e)
246
- for e in self._http.get_paginated(
247
- "https://api.anaplan.com/2/0/workspaces", "workspaces", params=params
248
- )
249
- ]
246
+ res = self._http.get_paginated(
247
+ "https://api.anaplan.com/2/0/workspaces", "workspaces", params=params
248
+ )
249
+ return [Workspace.model_validate(e) for e in res]
250
250
 
251
- def get_models(self, search_pattern: str | None = None) -> list[Model]:
251
+ def get_models(
252
+ self,
253
+ only_in_workspace: bool | str = False,
254
+ search_pattern: str | None = None,
255
+ sort_by: Literal["active_state", "name"] | None = None,
256
+ descending: bool = False,
257
+ ) -> list[Model]:
252
258
  """
253
259
  Lists all the Models the authenticated user has access to.
254
- :param search_pattern: Optionally filter for specific models. When provided,
260
+ :param only_in_workspace: If True, only lists models in the workspace provided when
261
+ instantiating the client. If a string is provided, only lists models in the workspace
262
+ with the given Id. If False (default), lists models in all workspaces the user
263
+ :param search_pattern: **Caution: This is an undocumented Feature and may behave
264
+ unpredictably. It requires the Tenant Admin role. For non-admin users, it is
265
+ ignored.** Optionally filter for specific models. When provided,
255
266
  case-insensitive matches model names containing this string.
256
267
  You can use the wildcards `%` for 0-n characters, and `_` for exactly 1 character.
257
268
  When None (default), returns all models.
269
+ :param sort_by: The field to sort the results by.
270
+ :param descending: If True, the results will be sorted in descending order.
258
271
  :return: The List of Models.
259
272
  """
260
- params = {"modelDetails": "true"}
273
+ params = {"modelDetails": "true"} | sort_params(sort_by, descending)
261
274
  if search_pattern:
262
275
  params["s"] = search_pattern
263
- return [
264
- Model.model_validate(e)
265
- for e in self._http.get_paginated(
266
- "https://api.anaplan.com/2/0/models", "models", params=params
267
- )
268
- ]
276
+ res = self._http.get_paginated(
277
+ models_url(only_in_workspace, self._workspace_id), "models", params=params
278
+ )
279
+ return [Model.model_validate(e) for e in res]
269
280
 
270
281
  def delete_models(self, model_ids: list[str]) -> ModelDeletionResult:
271
282
  """
@@ -281,56 +292,69 @@ class Client:
281
292
  )
282
293
  return ModelDeletionResult.model_validate(res)
283
294
 
284
- def get_files(self) -> list[File]:
295
+ def get_files(
296
+ self, sort_by: Literal["id", "name"] = "id", descending: bool = False
297
+ ) -> list[File]:
285
298
  """
286
299
  Lists all the Files in the Model.
300
+ :param sort_by: The field to sort the results by.
301
+ :param descending: If True, the results will be sorted in descending order.
287
302
  :return: The List of Files.
288
303
  """
289
- return [
290
- File.model_validate(e) for e in self._http.get_paginated(f"{self._url}/files", "files")
291
- ]
304
+ res = self._http.get_paginated(
305
+ f"{self._url}/files", "files", params=sort_params(sort_by, descending)
306
+ )
307
+ return [File.model_validate(e) for e in res]
292
308
 
293
- def get_actions(self) -> list[Action]:
309
+ def get_actions(self, sort_by: SortBy = None, descending: bool = False) -> list[Action]:
294
310
  """
295
311
  Lists all the Actions in the Model. This will only return the Actions listed under
296
312
  `Other Actions` in Anaplan. For Imports, exports, and processes, see their respective
297
313
  methods instead.
314
+ :param sort_by: The field to sort the results by.
315
+ :param descending: If True, the results will be sorted in descending order.
298
316
  :return: The List of Actions.
299
317
  """
300
- return [
301
- Action.model_validate(e)
302
- for e in self._http.get_paginated(f"{self._url}/actions", "actions")
303
- ]
318
+ res = self._http.get_paginated(
319
+ f"{self._url}/actions", "actions", params=sort_params(sort_by, descending)
320
+ )
321
+ return [Action.model_validate(e) for e in res]
304
322
 
305
- def get_processes(self) -> list[Process]:
323
+ def get_processes(self, sort_by: SortBy = None, descending: bool = False) -> list[Process]:
306
324
  """
307
325
  Lists all the Processes in the Model.
326
+ :param sort_by: The field to sort the results by.
327
+ :param descending: If True, the results will be sorted in descending order.
308
328
  :return: The List of Processes.
309
329
  """
310
- return [
311
- Process.model_validate(e)
312
- for e in self._http.get_paginated(f"{self._url}/processes", "processes")
313
- ]
330
+ res = self._http.get_paginated(
331
+ f"{self._url}/processes", "processes", params=sort_params(sort_by, descending)
332
+ )
333
+ return [Process.model_validate(e) for e in res]
314
334
 
315
- def get_imports(self) -> list[Import]:
335
+ def get_imports(self, sort_by: SortBy = None, descending: bool = False) -> list[Import]:
316
336
  """
317
337
  Lists all the Imports in the Model.
338
+ :param sort_by: The field to sort the results by.
339
+ :param descending: If True, the results will be sorted in descending order.
318
340
  :return: The List of Imports.
319
341
  """
320
- return [
321
- Import.model_validate(e)
322
- for e in self._http.get_paginated(f"{self._url}/imports", "imports")
323
- ]
342
+ res = self._http.get_paginated(
343
+ f"{self._url}/imports", "imports", params=sort_params(sort_by, descending)
344
+ )
345
+ return [Import.model_validate(e) for e in res]
324
346
 
325
- def get_exports(self) -> list[Export]:
347
+ def get_exports(self, sort_by: SortBy = None, descending: bool = False) -> list[Export]:
326
348
  """
327
349
  Lists all the Exports in the Model.
350
+ :param sort_by: The field to sort the results by.
351
+ :param descending: If True, the results will be sorted in descending order.
328
352
  :return: The List of Exports.
329
353
  """
330
- return [
331
- Export.model_validate(e)
332
- for e in (self._http.get(f"{self._url}/exports")).get("exports", [])
333
- ]
354
+ res = self._http.get_paginated(
355
+ f"{self._url}/exports", "exports", params=sort_params(sort_by, descending)
356
+ )
357
+ return [Export.model_validate(e) for e in res]
334
358
 
335
359
  def run_action(self, action_id: int, wait_for_completion: bool = True) -> TaskStatus:
336
360
  """
@@ -95,14 +95,15 @@ class _CloudWorksClient:
95
95
  logger.info(f"Deleted connection '{con_id}'.")
96
96
 
97
97
  def get_integrations(
98
- self, sort_by_name: Literal["ascending", "descending"] = "ascending"
98
+ self, sort_by: Literal["name"] | None = None, descending: bool = False
99
99
  ) -> list[Integration]:
100
100
  """
101
101
  List all integrations in CloudWorks.
102
- :param sort_by_name: Sort the integrations by name in ascending or descending order.
102
+ :param sort_by: The field to sort the results by.
103
+ :param descending: If True, the results will be sorted in descending order.
103
104
  :return: A list of integrations.
104
105
  """
105
- params = {"sortBy": "name" if sort_by_name == "ascending" else "-name"}
106
+ params = {"sortBy": f"{'-' if descending else ''}{sort_by}"} if sort_by else None
106
107
  return [
107
108
  Integration.model_validate(e)
108
109
  for e in self._http.get_paginated(f"{self._url}", "integrations", params=params)
@@ -7,6 +7,7 @@ from anaplan_sdk._services import (
7
7
  _HttpService,
8
8
  parse_calendar_response,
9
9
  parse_insertion_response,
10
+ sort_params,
10
11
  validate_dimension_id,
11
12
  )
12
13
  from anaplan_sdk.exceptions import InvalidIdentifierException
@@ -29,6 +30,8 @@ from anaplan_sdk.models import (
29
30
  ViewInfo,
30
31
  )
31
32
 
33
+ SortBy = Literal["id", "name"] | None
34
+
32
35
  logger = logging.getLogger("anaplan_sdk")
33
36
 
34
37
 
@@ -66,24 +69,32 @@ class _TransactionalClient:
66
69
  self._http.post_empty(f"{self._url}/close", headers={"Content-Type": "application/text"})
67
70
  logger.info(f"Closed model '{self._model_id}'.")
68
71
 
69
- def get_modules(self) -> list[Module]:
72
+ def get_modules(self, sort_by: SortBy = None, descending: bool = False) -> list[Module]:
70
73
  """
71
74
  Lists all the Modules in the Model.
75
+ :param sort_by: The field to sort the results by.
76
+ :param descending: If True, the results will be sorted in descending order.
72
77
  :return: The List of Modules.
73
78
  """
74
- return [
75
- Module.model_validate(e)
76
- for e in self._http.get_paginated(f"{self._url}/modules", "modules")
77
- ]
79
+ res = self._http.get_paginated(
80
+ f"{self._url}/modules", "modules", params=sort_params(sort_by, descending)
81
+ )
82
+ return [Module.model_validate(e) for e in res]
78
83
 
79
- def get_views(self) -> list[View]:
84
+ def get_views(
85
+ self, sort_by: Literal["id", "module_id", "name"] | None = None, descending: bool = False
86
+ ) -> list[View]:
80
87
  """
81
88
  Lists all the Views in the Model. This will include all Modules and potentially other saved
82
89
  views.
90
+ :param sort_by: The field to sort the results by.
91
+ :param descending: If True, the results will be sorted in descending order.
83
92
  :return: The List of Views.
84
93
  """
94
+ params = {"includesubsidiaryviews": True} | sort_params(sort_by, descending)
85
95
  return [
86
- View.model_validate(e) for e in self._http.get_paginated(f"{self._url}/views", "views")
96
+ View.model_validate(e)
97
+ for e in self._http.get_paginated(f"{self._url}/views", "views", params=params)
87
98
  ]
88
99
 
89
100
  def get_view_info(self, view_id: int) -> ViewInfo:
@@ -107,14 +118,17 @@ class _TransactionalClient:
107
118
  )
108
119
  return [LineItem.model_validate(e) for e in self._http.get(url).get("items", [])]
109
120
 
110
- def get_lists(self) -> list[List]:
121
+ def get_lists(self, sort_by: SortBy = None, descending: bool = False) -> list[List]:
111
122
  """
112
123
  Lists all the Lists in the Model.
124
+ :param sort_by: The field to sort the results by.
125
+ :param descending: If True, the results will be sorted in descending order.
113
126
  :return: All Lists on this model.
114
127
  """
115
- return [
116
- List.model_validate(e) for e in self._http.get_paginated(f"{self._url}/lists", "lists")
117
- ]
128
+ res = self._http.get_paginated(
129
+ f"{self._url}/lists", "lists", params=sort_params(sort_by, descending)
130
+ )
131
+ return [List.model_validate(e) for e in res]
118
132
 
119
133
  def get_list_metadata(self, list_id: int) -> ListMetadata:
120
134
  """
anaplan_sdk/_services.py CHANGED
@@ -33,6 +33,15 @@ from .models.cloud_works import (
33
33
  ScheduleInput,
34
34
  )
35
35
 
36
+ SORT_WARNING = (
37
+ "If you are sorting by a field that is potentially ambiguous (e.g., name), the order of "
38
+ "results is not guaranteed to be internally consistent across multiple requests. This will "
39
+ "lead to wrong results when paginating through result sets where the ambiguous order can cause "
40
+ "records to slip between pages or be duplicated on multiple pages. The only way to ensure "
41
+ "correct results when sorting is to make sure the entire result set fits in one page, or to "
42
+ "sort by a field that is guaranteed to be unique (e.g., id)."
43
+ )
44
+
36
45
  logger = logging.getLogger("anaplan_sdk")
37
46
 
38
47
  _json_header = {"Content-Type": "application/json"}
@@ -104,7 +113,8 @@ class _HttpService:
104
113
  if total_items <= self._page_size:
105
114
  logger.debug("All items fit in first page, no additional requests needed.")
106
115
  return iter(first_page)
107
-
116
+ if kwargs and (kwargs.get("params") or {}).get("sort", None):
117
+ logger.warning(SORT_WARNING)
108
118
  pages_needed = ceil(total_items / actual_size)
109
119
  logger.debug(f"Fetching {pages_needed - 1} additional pages with {actual_size} items each.")
110
120
  with ThreadPoolExecutor() as executor:
@@ -222,6 +232,8 @@ class _AsyncHttpService:
222
232
  if total_items <= self._page_size:
223
233
  logger.debug("All items fit in first page, no additional requests needed.")
224
234
  return iter(first_page)
235
+ if kwargs and (kwargs.get("params") or {}).get("sort", None):
236
+ logger.warning(SORT_WARNING)
225
237
  pages = await gather(
226
238
  *(
227
239
  self._get_page(url, actual_size, n * actual_size, result_key, **kwargs)
@@ -274,13 +286,29 @@ class _AsyncHttpService:
274
286
  raise AnaplanException("Exhausted all retries without a successful response or Error.")
275
287
 
276
288
 
277
- def sort_params(sort_by: str, descending: bool) -> dict[str, str | bool]:
289
+ def models_url(only_in_workspace: bool | str, workspace_id: str | None) -> str:
290
+ if isinstance(only_in_workspace, bool) and only_in_workspace:
291
+ if not workspace_id:
292
+ raise ValueError(
293
+ "Cannot list models in the current workspace, since no workspace Id was "
294
+ "provided when instantiating the client. Either provide a workspace Id when "
295
+ "instantiating the client, or pass a specific workspace Id to this method."
296
+ )
297
+ return f"https://api.anaplan.com/2/0/workspaces/{workspace_id}/models"
298
+ if isinstance(only_in_workspace, str):
299
+ return f"https://api.anaplan.com/2/0/workspaces/{only_in_workspace}/models"
300
+ return "https://api.anaplan.com/2/0/models"
301
+
302
+
303
+ def sort_params(sort_by: str | None, descending: bool) -> dict[str, str | bool]:
278
304
  """
279
305
  Construct search parameters for sorting. This also converts snake_case to camelCase.
280
306
  :param sort_by: The field to sort by, optionally in snake_case.
281
307
  :param descending: Whether to sort in descending order.
282
308
  :return: A dictionary of search parameters in Anaplan's expected format.
283
309
  """
310
+ if not sort_by:
311
+ return {}
284
312
  return {"sort": f"{'-' if descending else '+'}{to_camel(sort_by)}"}
285
313
 
286
314
 
@@ -39,11 +39,13 @@ class Model(AnaplanModel):
39
39
  description="The unique identifier of the user who last modified this model."
40
40
  )
41
41
  memory_usage: int = Field(0, description="The memory usage of this model in bytes.")
42
- current_workspace_id: str = Field(
43
- description="The unique identifier of the workspace that this model is currently in."
42
+ workspace_id: str = Field(
43
+ validation_alias="currentWorkspaceId",
44
+ description="The unique identifier of the workspace that this model is currently in.",
44
45
  )
45
- current_workspace_name: str = Field(
46
- description="The name of the workspace that this model is currently in."
46
+ workspace_name: str = Field(
47
+ validation_alias="currentWorkspaceName",
48
+ description="The name of the workspace that this model is currently in.",
47
49
  )
48
50
  url: str = Field(validation_alias="modelUrl", description="The current URL of this model.")
49
51
  category_values: list = Field(description="The category values of this model.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anaplan-sdk
3
- Version: 0.5.0a3
3
+ Version: 0.5.0a5
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
@@ -0,0 +1,30 @@
1
+ anaplan_sdk/__init__.py,sha256=WScEKtXlnRLjCb-j3qW9W4kEACTyPsTLFs-L54et2TQ,351
2
+ anaplan_sdk/_auth.py,sha256=l5z2WCcfQ05OkuQ1dcmikp6dB87Rw1qy2zu8bbaAQTs,16620
3
+ anaplan_sdk/_oauth.py,sha256=AynlJDrGIinQT0jwxI2RSvtU4D7Wasyw3H1uicdlLVI,12672
4
+ anaplan_sdk/_services.py,sha256=D54hGrzj1MSj7_6-WDZUZsoLcThI_neW4aIoDOqALjE,20869
5
+ anaplan_sdk/exceptions.py,sha256=ALkA9fBF0NQ7dufFxV6AivjmHyuJk9DOQ9jtJV2n7f0,1809
6
+ anaplan_sdk/_async_clients/__init__.py,sha256=pZXgMMg4S9Aj_pxQCaSiPuNG-sePVGBtNJ0133VjqW4,364
7
+ anaplan_sdk/_async_clients/_alm.py,sha256=zvKEvXlxNkcQim_XvyZLCbDafFldljg8APHqhAAIfvw,13147
8
+ anaplan_sdk/_async_clients/_audit.py,sha256=r3pJ4iHmXHyfDu19L80UTZzMReg6FKE7DlzyhXLhI8A,2896
9
+ anaplan_sdk/_async_clients/_bulk.py,sha256=JkXZ6mtK04bATyVZA6yA8akjVerurRjgINCViXAwoWM,30069
10
+ anaplan_sdk/_async_clients/_cloud_works.py,sha256=VB4l93426A0Xes5dZ6DsDu0go-BVNhs2RZn2zX5DSOc,17675
11
+ anaplan_sdk/_async_clients/_cw_flow.py,sha256=_allKIOP-qb33wrOj6GV5VAOvrCXOVJ1QXvck-jsocQ,3935
12
+ anaplan_sdk/_async_clients/_transactional.py,sha256=U6X5pW7By387JOgvHx-GmgVRi7MRJKALpx0lWI6xRMo,18024
13
+ anaplan_sdk/_clients/__init__.py,sha256=FsbwvZC1FHrxuRXwbPxUzbhz_lO1DpXIxEOjx6-3QuA,219
14
+ anaplan_sdk/_clients/_alm.py,sha256=3U7Cy5U5TsePF1YPogXvsOzNeQlQm_ezO5TlmD-Xbbs,12874
15
+ anaplan_sdk/_clients/_audit.py,sha256=oF1-7rGfYWG6LfM-i0vJzgpx4NAsLczo4welJR14N-U,2819
16
+ anaplan_sdk/_clients/_bulk.py,sha256=-I0e5yBHhQPIuS9pF_dRRi7OmY7H88P5mGPusGupKi4,30226
17
+ anaplan_sdk/_clients/_cloud_works.py,sha256=FsCp2wPxIoArAN1vcIfOI6ANNkK2ZebQ4MWJZB-nFJU,17466
18
+ anaplan_sdk/_clients/_cw_flow.py,sha256=O6t4utbDZdSVXGC0PXUcPpQ4oXrPohU9_8SUBCpxTXw,3824
19
+ anaplan_sdk/_clients/_transactional.py,sha256=SaHAnaGLZrhXmM8d6JnWWkwf-sVCEDW0nL2a4_wvjfk,17849
20
+ anaplan_sdk/models/__init__.py,sha256=zfwDQJQrXuLEXSpbJcm08a_YK1P7a7u-kMhwtJiJFmA,1783
21
+ anaplan_sdk/models/_alm.py,sha256=oeENd0YM7-LoIRBq2uATIQTxVgIP9rXx3UZE2UnQAp0,4670
22
+ anaplan_sdk/models/_base.py,sha256=6AZc9CfireUKgpZfMxYKu4MbwiyHQOsGLjKrxGXBLic,508
23
+ anaplan_sdk/models/_bulk.py,sha256=C0s6XdvHxuJHrPXU-pnZ1JXK1PJOl9FScHArpaox_mQ,8489
24
+ anaplan_sdk/models/_transactional.py,sha256=2bH10zvtMb5Lfh6DC7iQk72aEwq6tyLQ-XnH_0wYSqI,14172
25
+ anaplan_sdk/models/cloud_works.py,sha256=APUGDt_e-JshtXkba5cQh5rZkXOZBz0Aix0qVNdEWgw,19501
26
+ anaplan_sdk/models/flows.py,sha256=SuLgNj5-2SeE3U1i8iY8cq2IkjuUgd_3M1n2ENructk,3625
27
+ anaplan_sdk-0.5.0a5.dist-info/METADATA,sha256=cPMNYurLnr08YAdhsgOUFlrcw-xehz2gd5MQF_TrFg8,3678
28
+ anaplan_sdk-0.5.0a5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
+ anaplan_sdk-0.5.0a5.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
30
+ anaplan_sdk-0.5.0a5.dist-info/RECORD,,
@@ -1,30 +0,0 @@
1
- anaplan_sdk/__init__.py,sha256=WScEKtXlnRLjCb-j3qW9W4kEACTyPsTLFs-L54et2TQ,351
2
- anaplan_sdk/_auth.py,sha256=l5z2WCcfQ05OkuQ1dcmikp6dB87Rw1qy2zu8bbaAQTs,16620
3
- anaplan_sdk/_oauth.py,sha256=AynlJDrGIinQT0jwxI2RSvtU4D7Wasyw3H1uicdlLVI,12672
4
- anaplan_sdk/_services.py,sha256=gEeGUBAtmkp55-pZXPU1ehd57pGJkkVJJ6OB1oJ5awU,19252
5
- anaplan_sdk/exceptions.py,sha256=ALkA9fBF0NQ7dufFxV6AivjmHyuJk9DOQ9jtJV2n7f0,1809
6
- anaplan_sdk/_async_clients/__init__.py,sha256=pZXgMMg4S9Aj_pxQCaSiPuNG-sePVGBtNJ0133VjqW4,364
7
- anaplan_sdk/_async_clients/_alm.py,sha256=rhVhykUo6wIvA1SBQkpEAviSsVLURumi_3XQlxTf7z8,12788
8
- anaplan_sdk/_async_clients/_audit.py,sha256=dipSzp4jMvRCHJAVMQfO854_wpmIcYEDinEPSGdoms4,2342
9
- anaplan_sdk/_async_clients/_bulk.py,sha256=hjxJ4kY0IfyWYYLPu9QuFkxsPFGbcmY0XPru5LatTcs,30069
10
- anaplan_sdk/_async_clients/_cloud_works.py,sha256=ecm7DqT39J56xQwYxJMKd_ZVqxzXZdpmagwJUvqKBj4,17613
11
- anaplan_sdk/_async_clients/_cw_flow.py,sha256=_allKIOP-qb33wrOj6GV5VAOvrCXOVJ1QXvck-jsocQ,3935
12
- anaplan_sdk/_async_clients/_transactional.py,sha256=gO4v22O0RIHLYwfaVURie1-uLlXo0ypUlOloOZEgmtw,18038
13
- anaplan_sdk/_clients/__init__.py,sha256=FsbwvZC1FHrxuRXwbPxUzbhz_lO1DpXIxEOjx6-3QuA,219
14
- anaplan_sdk/_clients/_alm.py,sha256=_LlZIRCE3HxZ4OzU13LOGnX4MQ26j2puSPTy9WGJa3o,12515
15
- anaplan_sdk/_clients/_audit.py,sha256=9mq7VGYsl6wOdIU7G3GvzP3O7r1ZDCFg5eAu7k4RgxM,2154
16
- anaplan_sdk/_clients/_bulk.py,sha256=aY2uwAzXSMorEZr7FfP18QA_z0NV6ONQrqOOsyg8CsU,28108
17
- anaplan_sdk/_clients/_cloud_works.py,sha256=C4F_zJ70vIlZ1lPTCsHkbB41oO6vip9iXaQqRHo4FuY,17404
18
- anaplan_sdk/_clients/_cw_flow.py,sha256=O6t4utbDZdSVXGC0PXUcPpQ4oXrPohU9_8SUBCpxTXw,3824
19
- anaplan_sdk/_clients/_transactional.py,sha256=avqww59ccM3FqYMeK1oE-8UH4jyk_pKSCETzhSGKyxA,16936
20
- anaplan_sdk/models/__init__.py,sha256=zfwDQJQrXuLEXSpbJcm08a_YK1P7a7u-kMhwtJiJFmA,1783
21
- anaplan_sdk/models/_alm.py,sha256=oeENd0YM7-LoIRBq2uATIQTxVgIP9rXx3UZE2UnQAp0,4670
22
- anaplan_sdk/models/_base.py,sha256=6AZc9CfireUKgpZfMxYKu4MbwiyHQOsGLjKrxGXBLic,508
23
- anaplan_sdk/models/_bulk.py,sha256=S72qujNr5STdiyKaCEvrQjKYHik_aemiJFNKE7docpI,8405
24
- anaplan_sdk/models/_transactional.py,sha256=2bH10zvtMb5Lfh6DC7iQk72aEwq6tyLQ-XnH_0wYSqI,14172
25
- anaplan_sdk/models/cloud_works.py,sha256=APUGDt_e-JshtXkba5cQh5rZkXOZBz0Aix0qVNdEWgw,19501
26
- anaplan_sdk/models/flows.py,sha256=SuLgNj5-2SeE3U1i8iY8cq2IkjuUgd_3M1n2ENructk,3625
27
- anaplan_sdk-0.5.0a3.dist-info/METADATA,sha256=zd3-QsRJ_Fa9PTqn2wt0lMnLrJAkHuLi1YfmYnboeoE,3678
28
- anaplan_sdk-0.5.0a3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
- anaplan_sdk-0.5.0a3.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
30
- anaplan_sdk-0.5.0a3.dist-info/RECORD,,