ayon-python-api 1.2.19__tar.gz → 1.2.21__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/PKG-INFO +1 -1
  2. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/__init__.py +14 -0
  3. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api.py +190 -4
  4. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/links.py +1 -5
  5. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/lists.py +222 -1
  6. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/projects.py +19 -2
  7. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/constants.py +3 -4
  8. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/entity_hub.py +16 -7
  9. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/server_api.py +253 -177
  10. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/typing.py +10 -0
  11. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/utils.py +128 -13
  12. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/version.py +1 -1
  13. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_python_api.egg-info/PKG-INFO +1 -1
  14. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/pyproject.toml +2 -2
  15. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/LICENSE +0 -0
  16. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/README.md +0 -0
  17. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/__init__.py +0 -0
  18. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/actions.py +0 -0
  19. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/activities.py +0 -0
  20. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/attributes.py +0 -0
  21. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/base.py +0 -0
  22. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/bundles_addons.py +0 -0
  23. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/dependency_packages.py +0 -0
  24. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/events.py +0 -0
  25. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/folders.py +0 -0
  26. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/installers.py +0 -0
  27. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/products.py +0 -0
  28. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/representations.py +0 -0
  29. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/secrets.py +0 -0
  30. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/tasks.py +0 -0
  31. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/thumbnails.py +0 -0
  32. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/versions.py +0 -0
  33. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/_api_helpers/workfiles.py +0 -0
  34. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/events.py +0 -0
  35. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/exceptions.py +0 -0
  36. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/graphql.py +0 -0
  37. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/graphql_queries.py +0 -0
  38. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_api/operations.py +0 -0
  39. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_python_api.egg-info/SOURCES.txt +0 -0
  40. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  41. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_python_api.egg-info/requires.txt +0 -0
  42. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/ayon_python_api.egg-info/top_level.txt +0 -0
  43. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/setup.cfg +0 -0
  44. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/setup.py +0 -0
  45. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/tests/test_entity_hub.py +0 -0
  46. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/tests/test_folder_hierarchy.py +0 -0
  47. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/tests/test_get_events.py +0 -0
  48. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/tests/test_graphql_queries.py +0 -0
  49. {ayon_python_api-1.2.19 → ayon_python_api-1.2.21}/tests/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayon_python_api
3
- Version: 1.2.19
3
+ Version: 1.2.21
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -298,6 +298,13 @@ from ._api import (
298
298
  update_entity_list_items,
299
299
  update_entity_list_item,
300
300
  delete_entity_list_item,
301
+ get_entity_list_entities,
302
+ get_entity_list_folders_raw,
303
+ get_entity_list_folders,
304
+ create_entity_list_folder,
305
+ update_entity_list_folder,
306
+ delete_entity_list_folder,
307
+ set_entity_list_folders_order,
301
308
  get_thumbnail_by_id,
302
309
  get_thumbnail,
303
310
  get_folder_thumbnail,
@@ -609,6 +616,13 @@ __all__ = (
609
616
  "update_entity_list_items",
610
617
  "update_entity_list_item",
611
618
  "delete_entity_list_item",
619
+ "get_entity_list_entities",
620
+ "get_entity_list_folders_raw",
621
+ "get_entity_list_folders",
622
+ "create_entity_list_folder",
623
+ "update_entity_list_folder",
624
+ "delete_entity_list_folder",
625
+ "set_entity_list_folders_order",
612
626
  "get_thumbnail_by_id",
613
627
  "get_thumbnail",
614
628
  "get_folder_thumbnail",
@@ -48,8 +48,10 @@ if typing.TYPE_CHECKING:
48
48
  ActivityReferenceType,
49
49
  EntityListEntityType,
50
50
  EntityListItemMode,
51
+ EntityListScope,
51
52
  BackgroundOperationTask,
52
53
  LinkDirection,
54
+ CreateLinkData,
53
55
  EventFilter,
54
56
  EventStatus,
55
57
  EnrollEventData,
@@ -85,7 +87,6 @@ if typing.TYPE_CHECKING:
85
87
  EntityListAttributeDefinitionDict,
86
88
  AdvancedFilterDict,
87
89
  )
88
- from ._api_helpers.links import CreateLinkData
89
90
 
90
91
 
91
92
  class GlobalServerAPI(ServerAPI):
@@ -129,10 +130,10 @@ class GlobalServerAPI(ServerAPI):
129
130
  login is skipped.
130
131
 
131
132
  """
132
- previous_token = self._access_token
133
+ previous_token = self._token_info.token
133
134
  super().login(username, password)
134
- if self.has_valid_token and previous_token != self._access_token:
135
- os.environ[SERVER_API_ENV_KEY] = self._access_token
135
+ if self.has_valid_token and previous_token != self._token_info.token:
136
+ os.environ[SERVER_API_ENV_KEY] = self._token_info.token
136
137
 
137
138
  @staticmethod
138
139
  def get_url():
@@ -7981,6 +7982,7 @@ def create_entity_list(
7981
7982
  data: Optional[list[dict[str, Any]]] = None,
7982
7983
  tags: Optional[list[str]] = None,
7983
7984
  template: Optional[dict[str, Any]] = None,
7985
+ entity_list_folder_id: Optional[str] = None,
7984
7986
  owner: Optional[str] = None,
7985
7987
  active: Optional[bool] = None,
7986
7988
  items: Optional[list[dict[str, Any]]] = None,
@@ -8000,6 +8002,7 @@ def create_entity_list(
8000
8002
  data (Optional[dict[str, Any]]): Custom data of entity list.
8001
8003
  tags (Optional[list[str]]): Entity list tags.
8002
8004
  template (Optional[dict[str, Any]]): Dynamic list template.
8005
+ entity_list_folder_id (Optional[str]): Entity list folder id.
8003
8006
  owner (Optional[str]): New owner of the list.
8004
8007
  active (Optional[bool]): Change active state of entity list.
8005
8008
  items (Optional[list[dict[str, Any]]]): Initial items in
@@ -8018,6 +8021,7 @@ def create_entity_list(
8018
8021
  data=data,
8019
8022
  tags=tags,
8020
8023
  template=template,
8024
+ entity_list_folder_id=entity_list_folder_id,
8021
8025
  owner=owner,
8022
8026
  active=active,
8023
8027
  items=items,
@@ -8034,6 +8038,7 @@ def update_entity_list(
8034
8038
  attrib: Optional[list[dict[str, Any]]] = None,
8035
8039
  data: Optional[list[dict[str, Any]]] = None,
8036
8040
  tags: Optional[list[str]] = None,
8041
+ entity_list_folder_id: str | None | type[NOT_SET] = NOT_SET,
8037
8042
  owner: Optional[str] = None,
8038
8043
  active: Optional[bool] = None,
8039
8044
  ) -> None:
@@ -8048,6 +8053,9 @@ def update_entity_list(
8048
8053
  entity list.
8049
8054
  data (Optional[dict[str, Any]]): Custom data of entity list.
8050
8055
  tags (Optional[list[str]]): Entity list tags.
8056
+ entity_list_folder_id (str | None | type[NOT_SET]): New entity
8057
+ list folder id. Use ``None`` to move entity list to root.
8058
+ Use 'NOT_SET' to keep current folder.
8051
8059
  owner (Optional[str]): New owner of the list.
8052
8060
  active (Optional[bool]): Change active state of entity list.
8053
8061
 
@@ -8061,6 +8069,7 @@ def update_entity_list(
8061
8069
  attrib=attrib,
8062
8070
  data=data,
8063
8071
  tags=tags,
8072
+ entity_list_folder_id=entity_list_folder_id,
8064
8073
  owner=owner,
8065
8074
  active=active,
8066
8075
  )
@@ -8259,6 +8268,183 @@ def delete_entity_list_item(
8259
8268
  )
8260
8269
 
8261
8270
 
8271
+ def get_entity_list_entities(
8272
+ project_name: str,
8273
+ entity_list_id: str,
8274
+ ) -> dict[str, Any]:
8275
+ """Get entity list items using REST API.
8276
+
8277
+ Args:
8278
+ project_name (str): Project name.
8279
+ entity_list_id (str): Entity list id.
8280
+
8281
+ Returns:
8282
+ dict[str, Any]: Information about entities on the list.
8283
+
8284
+ """
8285
+ con = get_server_api_connection()
8286
+ return con.get_entity_list_entities(
8287
+ project_name=project_name,
8288
+ entity_list_id=entity_list_id,
8289
+ )
8290
+
8291
+
8292
+ def get_entity_list_folders_raw(
8293
+ project_name: str,
8294
+ ) -> dict[str, Any]:
8295
+ """Get entity list folders.
8296
+
8297
+ Args:
8298
+ project_name (str): Project name.
8299
+
8300
+ Returns:
8301
+ dict[str, Any]: Raw output of entity list folders output. At this
8302
+ moment contains only "folders" key with list of folders,
8303
+ but it can be extended in the future.
8304
+
8305
+ """
8306
+ con = get_server_api_connection()
8307
+ return con.get_entity_list_folders_raw(
8308
+ project_name=project_name,
8309
+ )
8310
+
8311
+
8312
+ def get_entity_list_folders(
8313
+ project_name: str,
8314
+ ) -> list[dict[str, Any]]:
8315
+ """Get entity list folders.
8316
+
8317
+ Returns:
8318
+ list[dict[str, Any]]: List of entity list folders.
8319
+
8320
+ """
8321
+ con = get_server_api_connection()
8322
+ return con.get_entity_list_folders(
8323
+ project_name=project_name,
8324
+ )
8325
+
8326
+
8327
+ def create_entity_list_folder(
8328
+ project_name: str,
8329
+ label: str,
8330
+ *,
8331
+ parent_id: str | None = None,
8332
+ color: str | None = None,
8333
+ icon: str | None = None,
8334
+ scope: list[EntityListScope] | None = None,
8335
+ data: dict[str, Any] | None = None,
8336
+ access: dict[str, Any] | None = None,
8337
+ entity_list_folder_id: str | None = None,
8338
+ ) -> str:
8339
+ """Create entity list folder.
8340
+
8341
+ Args:
8342
+ project_name (str): Project name.
8343
+ label (str): Folder label.
8344
+ parent_id (str | None): Parent folder id. If None, the folder will
8345
+ be created in root.
8346
+ color (str | None): Folder color.
8347
+ icon (str | None): Folder icon.
8348
+ scope (list[EntityListScope] | None): Folder scope. Empty list can
8349
+ be used to scope folder for all views.
8350
+ data (dict[str, Any] | None): Custom data of entity list folder.
8351
+ access (dict[str, Any] | None): Access control for
8352
+ entity list folder.
8353
+ entity_list_folder_id (str | None): Id of folder that will be
8354
+ created. If None, a new id will be generated.
8355
+
8356
+ Returns:
8357
+ str: Created entity list folder id.
8358
+
8359
+ """
8360
+ con = get_server_api_connection()
8361
+ return con.create_entity_list_folder(
8362
+ project_name=project_name,
8363
+ label=label,
8364
+ parent_id=parent_id,
8365
+ color=color,
8366
+ icon=icon,
8367
+ scope=scope,
8368
+ data=data,
8369
+ access=access,
8370
+ entity_list_folder_id=entity_list_folder_id,
8371
+ )
8372
+
8373
+
8374
+ def update_entity_list_folder(
8375
+ project_name: str,
8376
+ entity_list_folder_id: str,
8377
+ *,
8378
+ label: str | None = None,
8379
+ parent_id: str | None | type[NOT_SET] = NOT_SET,
8380
+ color: str | None = None,
8381
+ icon: str | None = None,
8382
+ scope: list[EntityListScope] | None = None,
8383
+ data: dict[str, Any] | None = None,
8384
+ access: dict[str, Any] | None = None,
8385
+ ) -> None:
8386
+ """Update entity list folder.
8387
+
8388
+ Args:
8389
+ project_name (str): Project name.
8390
+ entity_list_folder_id (str): Folder id that will be updated.
8391
+ label (str | None): New label of entity list folder.
8392
+ parent_id (str | None | type[NOT_SET]): New parent id of entity
8393
+ list folder. If None, the folder will be moved to root.
8394
+ color (str | None): New color of entity list folder.
8395
+ icon (str | None): New icon of entity list folder.
8396
+ scope (list[EntityListScope] | None): New scope of entity list
8397
+ folder. Empty list can be used to scope folder for all views.
8398
+ data (dict[str, Any] | None): Custom data of entity list folder.
8399
+ access (dict[str, Any] | None): Access control for
8400
+ entity list folder.
8401
+
8402
+ """
8403
+ con = get_server_api_connection()
8404
+ return con.update_entity_list_folder(
8405
+ project_name=project_name,
8406
+ entity_list_folder_id=entity_list_folder_id,
8407
+ label=label,
8408
+ parent_id=parent_id,
8409
+ color=color,
8410
+ icon=icon,
8411
+ scope=scope,
8412
+ data=data,
8413
+ access=access,
8414
+ )
8415
+
8416
+
8417
+ def delete_entity_list_folder(
8418
+ project_name: str,
8419
+ entity_list_folder_id: str,
8420
+ ) -> None:
8421
+ """Delete entity list folder.
8422
+ """
8423
+ con = get_server_api_connection()
8424
+ return con.delete_entity_list_folder(
8425
+ project_name=project_name,
8426
+ entity_list_folder_id=entity_list_folder_id,
8427
+ )
8428
+
8429
+
8430
+ def set_entity_list_folders_order(
8431
+ project_name: str,
8432
+ order: list[str],
8433
+ ) -> None:
8434
+ """Change order of entity list folders.
8435
+
8436
+ Args:
8437
+ project_name (str): Project name.
8438
+ order (list[str]): List of folder ids in desired order.
8439
+
8440
+ """
8441
+ con = get_server_api_connection()
8442
+ return con.set_entity_list_folders_order(
8443
+ project_name=project_name,
8444
+ order=order,
8445
+ )
8446
+
8447
+
8262
8448
  def get_thumbnail_by_id(
8263
8449
  project_name: str,
8264
8450
  thumbnail_id: str,
@@ -15,11 +15,7 @@ from ayon_api.graphql_queries import (
15
15
  from .base import BaseServerAPI
16
16
 
17
17
  if typing.TYPE_CHECKING:
18
- from typing import TypedDict
19
- from ayon_api.typing import LinkDirection
20
-
21
- class CreateLinkData(TypedDict):
22
- id: str
18
+ from ayon_api.typing import LinkDirection, CreateLinkData
23
19
 
24
20
 
25
21
  class LinksAPI(BaseServerAPI):
@@ -4,16 +4,18 @@ import json
4
4
  import typing
5
5
  from typing import Optional, Iterable, Any, Generator
6
6
 
7
- from ayon_api.utils import create_entity_id
7
+ from ayon_api.utils import NOT_SET, create_entity_id
8
8
  from ayon_api.graphql_queries import entity_lists_graphql_query
9
9
 
10
10
  from .base import BaseServerAPI
11
11
 
12
+
12
13
  if typing.TYPE_CHECKING:
13
14
  from ayon_api.typing import (
14
15
  EntityListEntityType,
15
16
  EntityListAttributeDefinitionDict,
16
17
  EntityListItemMode,
18
+ EntityListScope,
17
19
  )
18
20
 
19
21
 
@@ -159,6 +161,7 @@ class ListsAPI(BaseServerAPI):
159
161
  data: Optional[list[dict[str, Any]]] = None,
160
162
  tags: Optional[list[str]] = None,
161
163
  template: Optional[dict[str, Any]] = None,
164
+ entity_list_folder_id: Optional[str] = None,
162
165
  owner: Optional[str] = None,
163
166
  active: Optional[bool] = None,
164
167
  items: Optional[list[dict[str, Any]]] = None,
@@ -178,6 +181,7 @@ class ListsAPI(BaseServerAPI):
178
181
  data (Optional[dict[str, Any]]): Custom data of entity list.
179
182
  tags (Optional[list[str]]): Entity list tags.
180
183
  template (Optional[dict[str, Any]]): Dynamic list template.
184
+ entity_list_folder_id (Optional[str]): Entity list folder id.
181
185
  owner (Optional[str]): New owner of the list.
182
186
  active (Optional[bool]): Change active state of entity list.
183
187
  items (Optional[list[dict[str, Any]]]): Initial items in
@@ -199,6 +203,7 @@ class ListsAPI(BaseServerAPI):
199
203
  ("template", template),
200
204
  ("tags", tags),
201
205
  ("owner", owner),
206
+ ("entityListFolderId", entity_list_folder_id),
202
207
  ("data", data),
203
208
  ("active", active),
204
209
  ("items", items),
@@ -223,6 +228,7 @@ class ListsAPI(BaseServerAPI):
223
228
  attrib: Optional[list[dict[str, Any]]] = None,
224
229
  data: Optional[list[dict[str, Any]]] = None,
225
230
  tags: Optional[list[str]] = None,
231
+ entity_list_folder_id: str | None | type[NOT_SET] = NOT_SET,
226
232
  owner: Optional[str] = None,
227
233
  active: Optional[bool] = None,
228
234
  ) -> None:
@@ -237,6 +243,9 @@ class ListsAPI(BaseServerAPI):
237
243
  entity list.
238
244
  data (Optional[dict[str, Any]]): Custom data of entity list.
239
245
  tags (Optional[list[str]]): Entity list tags.
246
+ entity_list_folder_id (str | None | type[NOT_SET]): New entity
247
+ list folder id. Use ``None`` to move entity list to root.
248
+ Use 'NOT_SET' to keep current folder.
240
249
  owner (Optional[str]): New owner of the list.
241
250
  active (Optional[bool]): Change active state of entity list.
242
251
 
@@ -254,6 +263,9 @@ class ListsAPI(BaseServerAPI):
254
263
  )
255
264
  if value is not None
256
265
  }
266
+ if entity_list_folder_id is not NOT_SET:
267
+ kwargs["entityListFolderId"] = entity_list_folder_id
268
+
257
269
  response = self.patch(
258
270
  f"projects/{project_name}/lists/{list_id}",
259
271
  **kwargs
@@ -454,3 +466,212 @@ class ListsAPI(BaseServerAPI):
454
466
  f"projects/{project_name}/lists/{list_id}/items/{item_id}",
455
467
  )
456
468
  response.raise_for_status()
469
+
470
+ def get_entity_list_entities(
471
+ self, project_name: str, entity_list_id: str
472
+ ) -> dict[str, Any]:
473
+ """Get entity list items using REST API.
474
+
475
+ Args:
476
+ project_name (str): Project name.
477
+ entity_list_id (str): Entity list id.
478
+
479
+ Returns:
480
+ dict[str, Any]: Information about entities on the list.
481
+
482
+ """
483
+ response = self.get(
484
+ f"projects/{project_name}/lists/{entity_list_id}/entities"
485
+ )
486
+ response.raise_for_status()
487
+ return response.data
488
+
489
+ def get_entity_list_folders_raw(
490
+ self, project_name: str
491
+ ) -> dict[str, Any]:
492
+ """Get entity list folders.
493
+
494
+ Args:
495
+ project_name (str): Project name.
496
+
497
+ Returns:
498
+ dict[str, Any]: Raw output of entity list folders output. At this
499
+ moment contains only "folders" key with list of folders,
500
+ but it can be extended in the future.
501
+
502
+ """
503
+ response = self.get(f"projects/{project_name}/entityListFolders")
504
+ response.raise_for_status()
505
+ return response.data
506
+
507
+ def get_entity_list_folders(
508
+ self, project_name: str
509
+ ) -> list[dict[str, Any]]:
510
+ """Get entity list folders.
511
+
512
+ Returns:
513
+ list[dict[str, Any]]: List of entity list folders.
514
+
515
+ """
516
+ data = self.get_entity_list_folders_raw(project_name)
517
+ return data["folders"]
518
+
519
+ def create_entity_list_folder(
520
+ self,
521
+ project_name: str,
522
+ label: str,
523
+ *,
524
+ parent_id: str | None = None,
525
+ color: str | None = None,
526
+ icon: str | None = None,
527
+ scope: list[EntityListScope] | None = None,
528
+ data: dict[str, Any] | None = None,
529
+ access: dict[str, Any] | None = None,
530
+ entity_list_folder_id: str | None = None,
531
+ ) -> str:
532
+ """Create entity list folder.
533
+
534
+ Args:
535
+ project_name (str): Project name.
536
+ label (str): Folder label.
537
+ parent_id (str | None): Parent folder id. If None, the folder will
538
+ be created in root.
539
+ color (str | None): Folder color.
540
+ icon (str | None): Folder icon.
541
+ scope (list[EntityListScope] | None): Folder scope. Empty list can
542
+ be used to scope folder for all views.
543
+ data (dict[str, Any] | None): Custom data of entity list folder.
544
+ access (dict[str, Any] | None): Access control for
545
+ entity list folder.
546
+ entity_list_folder_id (str | None): Id of folder that will be
547
+ created. If None, a new id will be generated.
548
+
549
+ Returns:
550
+ str: Created entity list folder id.
551
+
552
+ """
553
+ if data is None:
554
+ data = {}
555
+
556
+ for key, value in (
557
+ ("color", color),
558
+ ("icon", icon),
559
+ ("scope", scope),
560
+ ):
561
+ if value:
562
+ data[key] = value
563
+
564
+ if not entity_list_folder_id:
565
+ entity_list_folder_id = create_entity_id()
566
+ body = {
567
+ "id": entity_list_folder_id,
568
+ "label": label,
569
+ }
570
+ if parent_id:
571
+ body["parentId"] = parent_id
572
+
573
+ if data:
574
+ body["data"] = data
575
+
576
+ if access:
577
+ body["access"] = access
578
+
579
+ response = self.post(
580
+ f"projects/{project_name}/entityListFolders",
581
+ **body
582
+ )
583
+ response.raise_for_status()
584
+ return entity_list_folder_id
585
+
586
+ def update_entity_list_folder(
587
+ self,
588
+ project_name: str,
589
+ entity_list_folder_id: str,
590
+ *,
591
+ label: str | None = None,
592
+ parent_id: str | None | type[NOT_SET] = NOT_SET,
593
+ color: str | None = None,
594
+ icon: str | None = None,
595
+ scope: list[EntityListScope] | None = None,
596
+ data: dict[str, Any] | None = None,
597
+ access: dict[str, Any] | None = None,
598
+ ) -> None:
599
+ """Update entity list folder.
600
+
601
+ Args:
602
+ project_name (str): Project name.
603
+ entity_list_folder_id (str): Folder id that will be updated.
604
+ label (str | None): New label of entity list folder.
605
+ parent_id (str | None | type[NOT_SET]): New parent id of entity
606
+ list folder. If None, the folder will be moved to root.
607
+ color (str | None): New color of entity list folder.
608
+ icon (str | None): New icon of entity list folder.
609
+ scope (list[EntityListScope] | None): New scope of entity list
610
+ folder. Empty list can be used to scope folder for all views.
611
+ data (dict[str, Any] | None): Custom data of entity list folder.
612
+ access (dict[str, Any] | None): Access control for
613
+ entity list folder.
614
+
615
+ """
616
+ if data is None:
617
+ data = {}
618
+
619
+ for key, value in (
620
+ ("color", color),
621
+ ("icon", icon),
622
+ ):
623
+ if value:
624
+ data[key] = value
625
+
626
+ if scope is not None:
627
+ data["scope"] = scope
628
+
629
+ body = {}
630
+ if data:
631
+ body["data"] = data
632
+ if label:
633
+ body["label"] = label
634
+ if access is not None:
635
+ body["access"] = access
636
+ if parent_id is not NOT_SET:
637
+ body["parentId"] = parent_id
638
+
639
+ if not body:
640
+ return
641
+
642
+ response = self.patch(
643
+ (
644
+ f"projects/{project_name}/"
645
+ f"entityListFolders/{entity_list_folder_id}"
646
+ ),
647
+ **body
648
+ )
649
+ response.raise_for_status()
650
+
651
+ def delete_entity_list_folder(
652
+ self,
653
+ project_name: str,
654
+ entity_list_folder_id: str,
655
+ ) -> None:
656
+ """Delete entity list folder."""
657
+ response = self.delete(
658
+ f"projects/{project_name}/"
659
+ f"entityListFolders/{entity_list_folder_id}"
660
+ )
661
+ response.raise_for_status()
662
+
663
+ def set_entity_list_folders_order(
664
+ self, project_name: str, order: list[str]
665
+ ) -> None:
666
+ """Change order of entity list folders.
667
+
668
+ Args:
669
+ project_name (str): Project name.
670
+ order (list[str]): List of folder ids in desired order.
671
+
672
+ """
673
+ response = self.post(
674
+ f"projects/{project_name}/entityListFolders/order",
675
+ order=order,
676
+ )
677
+ response.raise_for_status()
@@ -9,7 +9,6 @@ from typing import Optional, Generator, Iterable, Any
9
9
 
10
10
  from ayon_api.constants import (
11
11
  PROJECT_NAME_REGEX,
12
- DEFAULT_PRODUCT_BASE_TYPE_FIELDS,
13
12
  DEFAULT_PRODUCT_TYPE_FIELDS,
14
13
  )
15
14
  from ayon_api.utils import prepare_query_string, fill_own_attribs
@@ -50,6 +49,8 @@ class ProjectFetchType(Enum):
50
49
 
51
50
 
52
51
  class ProjectsAPI(BaseServerAPI):
52
+ _project_product_base_type_fields = None
53
+
53
54
  def get_project_anatomy_presets(self) -> list[AnatomyPresetDict]:
54
55
  """Anatomy presets available on server.
55
56
 
@@ -364,6 +365,7 @@ class ProjectsAPI(BaseServerAPI):
364
365
  graphql_project = next(self._get_graphql_projects(
365
366
  None,
366
367
  None,
368
+ include_skeleton=True,
367
369
  project_name=project_name,
368
370
  fields=graphql_fields,
369
371
  own_attributes=own_attributes,
@@ -801,7 +803,8 @@ class ProjectsAPI(BaseServerAPI):
801
803
  elif field == "productBaseTypes":
802
804
  must_use_graphql = True
803
805
  fields.discard(field)
804
- for f_name in DEFAULT_PRODUCT_BASE_TYPE_FIELDS:
806
+ # for f_name in DEFAULT_PRODUCT_BASE_TYPE_FIELDS:
807
+ for f_name in self._get_project_product_base_type_fields():
805
808
  graphql_fields.add(f"{field}.{f_name}")
806
809
 
807
810
  elif field.startswith("productBaseTypes"):
@@ -1016,3 +1019,17 @@ class ProjectsAPI(BaseServerAPI):
1016
1019
  )
1017
1020
  response.raise_for_status()
1018
1021
  return response.data
1022
+
1023
+ def _get_project_product_base_type_fields(self) -> set[str]:
1024
+ if self._project_product_base_type_fields is not None:
1025
+ return self._project_product_base_type_fields
1026
+
1027
+ graphql_schema = self.get_graphql_schema()
1028
+
1029
+ field_names = {"name"}
1030
+ for type_def in graphql_schema["__schema"]["types"]:
1031
+ if type_def["name"] == "ProductBaseType":
1032
+ field_names = {field["name"] for field in type_def["fields"]}
1033
+ break
1034
+ self._project_product_base_type_fields = field_names
1035
+ return field_names
@@ -75,15 +75,14 @@ DEFAULT_PROJECT_LINK_TYPES_FIELDS = {
75
75
  # --- Product types ---
76
76
  DEFAULT_PRODUCT_TYPE_FIELDS = {
77
77
  "name",
78
- "icon",
79
- "color",
80
78
  }
81
79
 
82
80
  # --- Product base type ---
83
81
  DEFAULT_PRODUCT_BASE_TYPE_FIELDS = {
84
- # Ignore 'icon' and 'color'
85
- # - current server implementation always returns 'null'
82
+ # TODO add 'icon' and 'color' when server supports it
86
83
  "name",
84
+ # "icon",
85
+ # "color",
87
86
  }
88
87
 
89
88
  # --- Project ---