Ayon-Python-Api 1.0.9__tar.gz → 1.0.10__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.
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/PKG-INFO +1 -1
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/__init__.py +8 -0
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/_api.py +93 -8
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/entity_hub.py +113 -28
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/events.py +0 -1
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/graphql.py +1 -1
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/graphql_queries.py +3 -1
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/server_api.py +225 -51
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/utils.py +50 -1
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/version.py +1 -1
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_python_api.egg-info/PKG-INFO +1 -1
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/pyproject.toml +5 -2
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/setup.py +1 -1
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/LICENSE +0 -0
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/README.md +0 -0
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/constants.py +0 -0
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/exceptions.py +0 -0
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_api/operations.py +0 -0
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_python_api.egg-info/SOURCES.txt +0 -0
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_python_api.egg-info/dependency_links.txt +0 -0
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_python_api.egg-info/requires.txt +0 -0
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_python_api.egg-info/top_level.txt +0 -0
- {ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/setup.cfg +0 -0
|
@@ -7,6 +7,8 @@ from .utils import (
|
|
|
7
7
|
is_token_valid,
|
|
8
8
|
validate_url,
|
|
9
9
|
login_to_server,
|
|
10
|
+
take_web_action_event,
|
|
11
|
+
abort_web_action_event,
|
|
10
12
|
)
|
|
11
13
|
from .server_api import (
|
|
12
14
|
RequestTypes,
|
|
@@ -85,6 +87,7 @@ from ._api import (
|
|
|
85
87
|
get_attributes_fields_for_type,
|
|
86
88
|
get_default_fields_for_type,
|
|
87
89
|
get_addons_info,
|
|
90
|
+
get_addon_endpoint,
|
|
88
91
|
get_addon_url,
|
|
89
92
|
download_addon_private_file,
|
|
90
93
|
get_installers,
|
|
@@ -103,6 +106,7 @@ from ._api import (
|
|
|
103
106
|
get_bundles,
|
|
104
107
|
create_bundle,
|
|
105
108
|
update_bundle,
|
|
109
|
+
check_bundle_compatibility,
|
|
106
110
|
delete_bundle,
|
|
107
111
|
get_project_anatomy_presets,
|
|
108
112
|
get_default_anatomy_preset_name,
|
|
@@ -238,6 +242,8 @@ __all__ = (
|
|
|
238
242
|
"is_token_valid",
|
|
239
243
|
"validate_url",
|
|
240
244
|
"login_to_server",
|
|
245
|
+
"take_web_action_event",
|
|
246
|
+
"abort_web_action_event",
|
|
241
247
|
|
|
242
248
|
"RequestTypes",
|
|
243
249
|
"ServerAPI",
|
|
@@ -313,6 +319,7 @@ __all__ = (
|
|
|
313
319
|
"get_attributes_fields_for_type",
|
|
314
320
|
"get_default_fields_for_type",
|
|
315
321
|
"get_addons_info",
|
|
322
|
+
"get_addon_endpoint",
|
|
316
323
|
"get_addon_url",
|
|
317
324
|
"download_addon_private_file",
|
|
318
325
|
"get_installers",
|
|
@@ -331,6 +338,7 @@ __all__ = (
|
|
|
331
338
|
"get_bundles",
|
|
332
339
|
"create_bundle",
|
|
333
340
|
"update_bundle",
|
|
341
|
+
"check_bundle_compatibility",
|
|
334
342
|
"delete_bundle",
|
|
335
343
|
"get_project_anatomy_presets",
|
|
336
344
|
"get_default_anatomy_preset_name",
|
|
@@ -780,11 +780,11 @@ def dispatch_event(*args, **kwargs):
|
|
|
780
780
|
sender (Optional[str]): Sender of event.
|
|
781
781
|
event_hash (Optional[str]): Event hash.
|
|
782
782
|
project_name (Optional[str]): Project name.
|
|
783
|
-
username (Optional[str]): Username which triggered event.
|
|
784
783
|
depends_on (Optional[str]): Add dependency to another event.
|
|
784
|
+
username (Optional[str]): Username which triggered event.
|
|
785
785
|
description (Optional[str]): Description of event.
|
|
786
|
-
summary (Optional[dict[str, Any]]): Summary of event that can
|
|
787
|
-
for simple filtering on listeners.
|
|
786
|
+
summary (Optional[dict[str, Any]]): Summary of event that can
|
|
787
|
+
be used for simple filtering on listeners.
|
|
788
788
|
payload (Optional[dict[str, Any]]): Full payload of event data with
|
|
789
789
|
all details.
|
|
790
790
|
finished (Optional[bool]): Mark event as finished on dispatch.
|
|
@@ -875,7 +875,8 @@ def download_file_to_stream(*args, **kwargs):
|
|
|
875
875
|
|
|
876
876
|
Args:
|
|
877
877
|
endpoint (str): Endpoint or URL to file that should be downloaded.
|
|
878
|
-
stream (Union[io.BytesIO, BinaryIO]): Stream where output will
|
|
878
|
+
stream (Union[io.BytesIO, BinaryIO]): Stream where output will
|
|
879
|
+
be stored.
|
|
879
880
|
chunk_size (Optional[int]): Size of chunks that are received
|
|
880
881
|
in single loop.
|
|
881
882
|
progress (Optional[TransferProgress]): Object that gives ability
|
|
@@ -1155,6 +1156,30 @@ def get_addons_info(*args, **kwargs):
|
|
|
1155
1156
|
return con.get_addons_info(*args, **kwargs)
|
|
1156
1157
|
|
|
1157
1158
|
|
|
1159
|
+
def get_addon_endpoint(*args, **kwargs):
|
|
1160
|
+
"""Calculate endpoint to addon route.
|
|
1161
|
+
|
|
1162
|
+
Examples:
|
|
1163
|
+
|
|
1164
|
+
>>> api = ServerAPI("https://your.url.com")
|
|
1165
|
+
>>> api.get_addon_url(
|
|
1166
|
+
... "example", "1.0.0", "private", "my.zip")
|
|
1167
|
+
'addons/example/1.0.0/private/my.zip'
|
|
1168
|
+
|
|
1169
|
+
Args:
|
|
1170
|
+
addon_name (str): Name of addon.
|
|
1171
|
+
addon_version (str): Version of addon.
|
|
1172
|
+
*subpaths (str): Any amount of subpaths that are added to
|
|
1173
|
+
addon url.
|
|
1174
|
+
|
|
1175
|
+
Returns:
|
|
1176
|
+
str: Final url.
|
|
1177
|
+
|
|
1178
|
+
"""
|
|
1179
|
+
con = get_server_api_connection()
|
|
1180
|
+
return con.get_addon_endpoint(*args, **kwargs)
|
|
1181
|
+
|
|
1182
|
+
|
|
1158
1183
|
def get_addon_url(*args, **kwargs):
|
|
1159
1184
|
"""Calculate url to addon route.
|
|
1160
1185
|
|
|
@@ -1163,13 +1188,14 @@ def get_addon_url(*args, **kwargs):
|
|
|
1163
1188
|
>>> api = ServerAPI("https://your.url.com")
|
|
1164
1189
|
>>> api.get_addon_url(
|
|
1165
1190
|
... "example", "1.0.0", "private", "my.zip")
|
|
1166
|
-
'https://your.url.com/addons/example/1.0.0/private/my.zip'
|
|
1191
|
+
'https://your.url.com/api/addons/example/1.0.0/private/my.zip'
|
|
1167
1192
|
|
|
1168
1193
|
Args:
|
|
1169
1194
|
addon_name (str): Name of addon.
|
|
1170
1195
|
addon_version (str): Version of addon.
|
|
1171
1196
|
*subpaths (str): Any amount of subpaths that are added to
|
|
1172
1197
|
addon url.
|
|
1198
|
+
use_rest (Optional[bool]): Use rest endpoint.
|
|
1173
1199
|
|
|
1174
1200
|
Returns:
|
|
1175
1201
|
str: Final url.
|
|
@@ -1504,7 +1530,23 @@ def create_bundle(*args, **kwargs):
|
|
|
1504
1530
|
"""Create bundle on server.
|
|
1505
1531
|
|
|
1506
1532
|
Bundle cannot be changed once is created. Only isProduction, isStaging
|
|
1507
|
-
and dependency packages can change after creation.
|
|
1533
|
+
and dependency packages can change after creation. In case dev bundle
|
|
1534
|
+
is created, it is possible to change anything, but it is not possible
|
|
1535
|
+
to mark bundle as dev and production or staging at the same time.
|
|
1536
|
+
|
|
1537
|
+
Development addon config can define custom path to client code. It is
|
|
1538
|
+
used only for dev bundles.
|
|
1539
|
+
|
|
1540
|
+
Example of 'dev_addons_config'::
|
|
1541
|
+
|
|
1542
|
+
```json
|
|
1543
|
+
{
|
|
1544
|
+
"core": {
|
|
1545
|
+
"enabled": true,
|
|
1546
|
+
"path": "/path/to/ayon-core/client"
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
```
|
|
1508
1550
|
|
|
1509
1551
|
Args:
|
|
1510
1552
|
name (str): Name of bundle.
|
|
@@ -1516,6 +1558,11 @@ def create_bundle(*args, **kwargs):
|
|
|
1516
1558
|
is_production (Optional[bool]): Bundle will be marked as
|
|
1517
1559
|
production.
|
|
1518
1560
|
is_staging (Optional[bool]): Bundle will be marked as staging.
|
|
1561
|
+
is_dev (Optional[bool]): Bundle will be marked as dev.
|
|
1562
|
+
dev_active_user (Optional[str]): Username that will be assigned
|
|
1563
|
+
to dev bundle. Can be used only if 'is_dev' is set to 'True'.
|
|
1564
|
+
dev_addons_config (Optional[dict[str, Any]]): Configuration for
|
|
1565
|
+
dev addons. Can be used only if 'is_dev' is set to 'True'.
|
|
1519
1566
|
|
|
1520
1567
|
"""
|
|
1521
1568
|
con = get_server_api_connection()
|
|
@@ -1531,17 +1578,55 @@ def update_bundle(*args, **kwargs):
|
|
|
1531
1578
|
|
|
1532
1579
|
Args:
|
|
1533
1580
|
bundle_name (str): Name of bundle.
|
|
1581
|
+
addon_versions (Optional[dict[str, str]]): Addon versions,
|
|
1582
|
+
possible only for dev bundles.
|
|
1583
|
+
installer_version (Optional[str]): Installer version, possible
|
|
1584
|
+
only for dev bundles.
|
|
1534
1585
|
dependency_packages (Optional[dict[str, str]]): Dependency pacakge
|
|
1535
1586
|
names that should be used with the bundle.
|
|
1536
1587
|
is_production (Optional[bool]): Bundle will be marked as
|
|
1537
1588
|
production.
|
|
1538
1589
|
is_staging (Optional[bool]): Bundle will be marked as staging.
|
|
1590
|
+
is_dev (Optional[bool]): Bundle will be marked as dev.
|
|
1591
|
+
dev_active_user (Optional[str]): Username that will be assigned
|
|
1592
|
+
to dev bundle. Can be used only for dev bundles.
|
|
1593
|
+
dev_addons_config (Optional[dict[str, Any]]): Configuration for
|
|
1594
|
+
dev addons. Can be used only for dev bundles.
|
|
1539
1595
|
|
|
1540
1596
|
"""
|
|
1541
1597
|
con = get_server_api_connection()
|
|
1542
1598
|
return con.update_bundle(*args, **kwargs)
|
|
1543
1599
|
|
|
1544
1600
|
|
|
1601
|
+
def check_bundle_compatibility(*args, **kwargs):
|
|
1602
|
+
"""Check bundle compatibility.
|
|
1603
|
+
|
|
1604
|
+
Can be used as per-flight validation before creating bundle.
|
|
1605
|
+
|
|
1606
|
+
Args:
|
|
1607
|
+
name (str): Name of bundle.
|
|
1608
|
+
addon_versions (dict[str, str]): Addon versions.
|
|
1609
|
+
installer_version (Union[str, None]): Installer version.
|
|
1610
|
+
dependency_packages (Optional[dict[str, str]]): Dependency
|
|
1611
|
+
package names. Keys are platform names and values are name of
|
|
1612
|
+
packages.
|
|
1613
|
+
is_production (Optional[bool]): Bundle will be marked as
|
|
1614
|
+
production.
|
|
1615
|
+
is_staging (Optional[bool]): Bundle will be marked as staging.
|
|
1616
|
+
is_dev (Optional[bool]): Bundle will be marked as dev.
|
|
1617
|
+
dev_active_user (Optional[str]): Username that will be assigned
|
|
1618
|
+
to dev bundle. Can be used only if 'is_dev' is set to 'True'.
|
|
1619
|
+
dev_addons_config (Optional[dict[str, Any]]): Configuration for
|
|
1620
|
+
dev addons. Can be used only if 'is_dev' is set to 'True'.
|
|
1621
|
+
|
|
1622
|
+
Returns:
|
|
1623
|
+
Dict[str, Any]: Server response, with 'success' and 'issues'.
|
|
1624
|
+
|
|
1625
|
+
"""
|
|
1626
|
+
con = get_server_api_connection()
|
|
1627
|
+
return con.check_bundle_compatibility(*args, **kwargs)
|
|
1628
|
+
|
|
1629
|
+
|
|
1545
1630
|
def delete_bundle(*args, **kwargs):
|
|
1546
1631
|
"""Delete bundle from server.
|
|
1547
1632
|
|
|
@@ -2367,8 +2452,8 @@ def get_folders(*args, **kwargs):
|
|
|
2367
2452
|
"""Query folders from server.
|
|
2368
2453
|
|
|
2369
2454
|
Todos:
|
|
2370
|
-
Folder name won't be unique identifier, so we should add
|
|
2371
|
-
filtering.
|
|
2455
|
+
Folder name won't be unique identifier, so we should add
|
|
2456
|
+
folder path filtering.
|
|
2372
2457
|
|
|
2373
2458
|
Notes:
|
|
2374
2459
|
Filter 'active' don't have direct filter in GraphQl.
|
|
@@ -2,10 +2,17 @@ import re
|
|
|
2
2
|
import copy
|
|
3
3
|
import collections
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
|
+
import typing
|
|
5
6
|
|
|
6
7
|
from ._api import get_server_api_connection
|
|
7
8
|
from .utils import create_entity_id, convert_entity_id, slugify_string
|
|
8
9
|
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from typing import Literal
|
|
12
|
+
|
|
13
|
+
StatusState = Literal["not_started", "in_progress", "done", "blocked"]
|
|
14
|
+
EntityType = Literal["project", "folder", "task"]
|
|
15
|
+
|
|
9
16
|
|
|
10
17
|
class _CustomNone(object):
|
|
11
18
|
def __init__(self, name=None):
|
|
@@ -44,7 +51,7 @@ class EntityHub(object):
|
|
|
44
51
|
key on entities. This is not recommended as 'data' may be use for
|
|
45
52
|
secure information and would also slow down server queries. Content
|
|
46
53
|
of 'data' key can't be received only GraphQl.
|
|
47
|
-
|
|
54
|
+
|
|
48
55
|
"""
|
|
49
56
|
|
|
50
57
|
def __init__(
|
|
@@ -129,8 +136,8 @@ class EntityHub(object):
|
|
|
129
136
|
Use attribute schema to validate values on entities.
|
|
130
137
|
|
|
131
138
|
Args:
|
|
132
|
-
entity_type (
|
|
133
|
-
|
|
139
|
+
entity_type (EntityType): Entity type for which should
|
|
140
|
+
be attributes received.
|
|
134
141
|
|
|
135
142
|
Returns:
|
|
136
143
|
Dict[str, Dict[str, Any]]: Attribute schemas that are available
|
|
@@ -259,8 +266,8 @@ class EntityHub(object):
|
|
|
259
266
|
Args:
|
|
260
267
|
folder_type (str): Type of folder. Folder type must be available in
|
|
261
268
|
config of project folder types.
|
|
262
|
-
entity_id (Union[str, None]): Id of the entity. New id is created
|
|
263
|
-
not passed.
|
|
269
|
+
entity_id (Union[str, None]): Id of the entity. New id is created
|
|
270
|
+
if not passed.
|
|
264
271
|
parent_id (Union[str, None]): Id of parent entity.
|
|
265
272
|
name (str): Name of entity.
|
|
266
273
|
label (Optional[str]): Folder label.
|
|
@@ -289,8 +296,8 @@ class EntityHub(object):
|
|
|
289
296
|
Args:
|
|
290
297
|
task_type (str): Type of task. Task type must be available in
|
|
291
298
|
config of project folder types.
|
|
292
|
-
entity_id (Union[str, None]): Id of the entity. New id is created
|
|
293
|
-
not passed.
|
|
299
|
+
entity_id (Union[str, None]): Id of the entity. New id is created
|
|
300
|
+
if not passed.
|
|
294
301
|
parent_id (Union[str, None]): Id of parent entity.
|
|
295
302
|
name (str): Name of entity.
|
|
296
303
|
label (Optional[str]): Folder label.
|
|
@@ -579,10 +586,16 @@ class EntityHub(object):
|
|
|
579
586
|
raise ValueError(
|
|
580
587
|
"Project \"{}\" was not found.".format(project_name)
|
|
581
588
|
)
|
|
582
|
-
|
|
589
|
+
major, minor, _, _, _ = self._connection.get_server_version_tuple()
|
|
590
|
+
status_scope_supported = True
|
|
591
|
+
if (major, minor) < (1, 5):
|
|
592
|
+
status_scope_supported = False
|
|
583
593
|
self._project_entity = ProjectEntity.from_entity_data(
|
|
584
594
|
project, self
|
|
585
595
|
)
|
|
596
|
+
self._project_entity.set_status_scope_supported(
|
|
597
|
+
status_scope_supported
|
|
598
|
+
)
|
|
586
599
|
|
|
587
600
|
self.add_entity(self._project_entity)
|
|
588
601
|
return self._project_entity
|
|
@@ -734,8 +747,8 @@ class EntityHub(object):
|
|
|
734
747
|
Args:
|
|
735
748
|
project_changes (dict[str, Any]): Project changes.
|
|
736
749
|
orig_types (list[dict[str, Any]]): Original types.
|
|
737
|
-
changes_key (Literal[folderTypes, taskTypes]): Key of type
|
|
738
|
-
in project changes.
|
|
750
|
+
changes_key (Literal["folderTypes", "taskTypes"]): Key of type
|
|
751
|
+
changes in project changes.
|
|
739
752
|
post_changes (dict[str, Any]): An object where post changes will
|
|
740
753
|
be stored.
|
|
741
754
|
|
|
@@ -1247,7 +1260,7 @@ class BaseEntity(ABC):
|
|
|
1247
1260
|
"""Entity type coresponding to server.
|
|
1248
1261
|
|
|
1249
1262
|
Returns:
|
|
1250
|
-
|
|
1263
|
+
EntityType: Entity type.
|
|
1251
1264
|
|
|
1252
1265
|
"""
|
|
1253
1266
|
pass
|
|
@@ -1473,7 +1486,7 @@ class BaseEntity(ABC):
|
|
|
1473
1486
|
|
|
1474
1487
|
Raises:
|
|
1475
1488
|
TypeError: If validation of parent does not pass.
|
|
1476
|
-
|
|
1489
|
+
|
|
1477
1490
|
"""
|
|
1478
1491
|
parent_id = None
|
|
1479
1492
|
if parent is not None:
|
|
@@ -1604,16 +1617,19 @@ class ProjectStatus:
|
|
|
1604
1617
|
Args:
|
|
1605
1618
|
name (str): Name of the status. e.g. 'In progress'
|
|
1606
1619
|
short_name (Optional[str]): Short name of the status. e.g. 'IP'
|
|
1607
|
-
state (Optional[
|
|
1608
|
-
state of the status.
|
|
1620
|
+
state (Optional[StatusState]): A state of the status.
|
|
1609
1621
|
icon (Optional[str]): Icon of the status. e.g. 'play_arrow'.
|
|
1610
1622
|
color (Optional[str]): Color of the status. e.g. '#eeeeee'.
|
|
1623
|
+
scope (Optional[Iterable[str]]): Scope of the status. e.g. ['folder'].
|
|
1611
1624
|
index (Optional[int]): Index of the status.
|
|
1612
1625
|
project_statuses (Optional[_ProjectStatuses]): Project statuses
|
|
1613
1626
|
wrapper.
|
|
1614
1627
|
|
|
1615
1628
|
"""
|
|
1616
|
-
valid_states =
|
|
1629
|
+
valid_states = {"not_started", "in_progress", "done", "blocked"}
|
|
1630
|
+
valid_scope = {
|
|
1631
|
+
"folder", "task", "product", "version", "representation", "workfile"
|
|
1632
|
+
}
|
|
1617
1633
|
color_regex = re.compile(r"#([a-f0-9]{6})$")
|
|
1618
1634
|
default_state = "in_progress"
|
|
1619
1635
|
default_color = "#eeeeee"
|
|
@@ -1625,6 +1641,7 @@ class ProjectStatus:
|
|
|
1625
1641
|
state=None,
|
|
1626
1642
|
icon=None,
|
|
1627
1643
|
color=None,
|
|
1644
|
+
scope=None,
|
|
1628
1645
|
index=None,
|
|
1629
1646
|
project_statuses=None,
|
|
1630
1647
|
is_new=None,
|
|
@@ -1633,12 +1650,16 @@ class ProjectStatus:
|
|
|
1633
1650
|
icon = icon or ""
|
|
1634
1651
|
state = state or self.default_state
|
|
1635
1652
|
color = color or self.default_color
|
|
1653
|
+
if scope is None:
|
|
1654
|
+
scope = self.valid_scope
|
|
1655
|
+
scope = set(scope)
|
|
1636
1656
|
self._name = name
|
|
1637
1657
|
self._short_name = short_name
|
|
1638
1658
|
self._icon = icon
|
|
1639
1659
|
self._slugified_name = None
|
|
1640
1660
|
self._state = None
|
|
1641
1661
|
self._color = None
|
|
1662
|
+
self._scope = scope
|
|
1642
1663
|
self.set_state(state)
|
|
1643
1664
|
self.set_color(color)
|
|
1644
1665
|
|
|
@@ -1647,6 +1668,7 @@ class ProjectStatus:
|
|
|
1647
1668
|
self._original_icon = icon
|
|
1648
1669
|
self._original_state = state
|
|
1649
1670
|
self._original_color = color
|
|
1671
|
+
self._original_scope = set(scope)
|
|
1650
1672
|
self._original_index = index
|
|
1651
1673
|
|
|
1652
1674
|
self._index = index
|
|
@@ -1690,8 +1712,14 @@ class ProjectStatus:
|
|
|
1690
1712
|
self._original_icon = self.icon
|
|
1691
1713
|
self._original_state = self.state
|
|
1692
1714
|
self._original_color = self.color
|
|
1715
|
+
self._original_scope = self.scope
|
|
1693
1716
|
self._original_index = self.index
|
|
1694
1717
|
|
|
1718
|
+
def is_available_for_entity_type(self, entity_type):
|
|
1719
|
+
if self._scope is None:
|
|
1720
|
+
return True
|
|
1721
|
+
return entity_type in self._scope
|
|
1722
|
+
|
|
1695
1723
|
@staticmethod
|
|
1696
1724
|
def slugify_name(name):
|
|
1697
1725
|
"""Slugify status name for name comparison.
|
|
@@ -1750,6 +1778,7 @@ class ProjectStatus:
|
|
|
1750
1778
|
or self._original_state != self._state
|
|
1751
1779
|
or self._original_icon != self._icon
|
|
1752
1780
|
or self._original_color != self._color
|
|
1781
|
+
or self._original_scope != self._scope
|
|
1753
1782
|
)
|
|
1754
1783
|
|
|
1755
1784
|
def delete(self):
|
|
@@ -1864,8 +1893,7 @@ class ProjectStatus:
|
|
|
1864
1893
|
"""Get state of project status.
|
|
1865
1894
|
|
|
1866
1895
|
Return:
|
|
1867
|
-
|
|
1868
|
-
state of status.
|
|
1896
|
+
StatusState: General state of status.
|
|
1869
1897
|
|
|
1870
1898
|
"""
|
|
1871
1899
|
return self._state
|
|
@@ -1874,8 +1902,7 @@ class ProjectStatus:
|
|
|
1874
1902
|
"""Set color of project status.
|
|
1875
1903
|
|
|
1876
1904
|
Args:
|
|
1877
|
-
state (
|
|
1878
|
-
state of status.
|
|
1905
|
+
state (StatusState): General state of status.
|
|
1879
1906
|
|
|
1880
1907
|
"""
|
|
1881
1908
|
if state not in self.valid_states:
|
|
@@ -1906,6 +1933,36 @@ class ProjectStatus:
|
|
|
1906
1933
|
raise ValueError("Invalid color value '{}'".format(color))
|
|
1907
1934
|
self._color = color
|
|
1908
1935
|
|
|
1936
|
+
def get_scope(self):
|
|
1937
|
+
"""Get scope of the status.
|
|
1938
|
+
|
|
1939
|
+
Returns:
|
|
1940
|
+
Set[str]: Scope of the status.
|
|
1941
|
+
|
|
1942
|
+
"""
|
|
1943
|
+
return set(self._scope)
|
|
1944
|
+
|
|
1945
|
+
def set_scope(self, scope):
|
|
1946
|
+
"""Get scope of the status.
|
|
1947
|
+
|
|
1948
|
+
Returns:
|
|
1949
|
+
scope (Iterable[str]): Scope of the status.
|
|
1950
|
+
|
|
1951
|
+
"""
|
|
1952
|
+
if not isinstance(scope, (list, set, tuple)):
|
|
1953
|
+
raise TypeError(
|
|
1954
|
+
f"Scope must be a list, set, tuple. Got '{type(scope)}'."
|
|
1955
|
+
)
|
|
1956
|
+
|
|
1957
|
+
scope = set(scope)
|
|
1958
|
+
invalid_entity_types = scope - self.valid_scope
|
|
1959
|
+
if invalid_entity_types:
|
|
1960
|
+
raise ValueError("Invalid scope values '{}'".format(
|
|
1961
|
+
", ".join(invalid_entity_types)
|
|
1962
|
+
))
|
|
1963
|
+
|
|
1964
|
+
self._scope = scope
|
|
1965
|
+
|
|
1909
1966
|
name = property(get_name, set_name)
|
|
1910
1967
|
short_name = property(get_short_name, set_short_name)
|
|
1911
1968
|
project_statuses = property(get_project_statuses, set_project_statuses)
|
|
@@ -1913,6 +1970,7 @@ class ProjectStatus:
|
|
|
1913
1970
|
state = property(get_state, set_state)
|
|
1914
1971
|
color = property(get_color, set_color)
|
|
1915
1972
|
icon = property(get_icon, set_icon)
|
|
1973
|
+
scope = property(get_scope, set_scope)
|
|
1916
1974
|
|
|
1917
1975
|
def _validate_other_p_statuses(self, other):
|
|
1918
1976
|
"""Validate if other status can be used for move.
|
|
@@ -1977,6 +2035,7 @@ class ProjectStatus:
|
|
|
1977
2035
|
"state": self.state,
|
|
1978
2036
|
"icon": self.icon,
|
|
1979
2037
|
"color": self.color,
|
|
2038
|
+
"scope": list(self._scope),
|
|
1980
2039
|
}
|
|
1981
2040
|
if (
|
|
1982
2041
|
not self._is_new
|
|
@@ -2003,6 +2062,7 @@ class ProjectStatus:
|
|
|
2003
2062
|
data.get("state"),
|
|
2004
2063
|
data.get("icon"),
|
|
2005
2064
|
data.get("color"),
|
|
2065
|
+
data.get("scope"),
|
|
2006
2066
|
index=index,
|
|
2007
2067
|
project_statuses=project_statuses
|
|
2008
2068
|
)
|
|
@@ -2026,6 +2086,7 @@ class _ProjectStatuses:
|
|
|
2026
2086
|
ProjectStatus.from_data(status, idx, self)
|
|
2027
2087
|
for idx, status in enumerate(statuses)
|
|
2028
2088
|
]
|
|
2089
|
+
self._scope_supported = False
|
|
2029
2090
|
self._orig_status_length = len(self._statuses)
|
|
2030
2091
|
self._set_called = False
|
|
2031
2092
|
|
|
@@ -2049,27 +2110,31 @@ class _ProjectStatuses:
|
|
|
2049
2110
|
state=None,
|
|
2050
2111
|
icon=None,
|
|
2051
2112
|
color=None,
|
|
2113
|
+
scope=None,
|
|
2052
2114
|
):
|
|
2053
2115
|
"""Create project status.
|
|
2054
2116
|
|
|
2055
2117
|
Args:
|
|
2056
2118
|
name (str): Name of the status. e.g. 'In progress'
|
|
2057
2119
|
short_name (Optional[str]): Short name of the status. e.g. 'IP'
|
|
2058
|
-
state (Optional[
|
|
2059
|
-
state of the status.
|
|
2120
|
+
state (Optional[StatusState]): A state of the status.
|
|
2060
2121
|
icon (Optional[str]): Icon of the status. e.g. 'play_arrow'.
|
|
2061
2122
|
color (Optional[str]): Color of the status. e.g. '#eeeeee'.
|
|
2123
|
+
scope (Optional[List[str]]): Scope of the status. e.g. ['folder'].
|
|
2062
2124
|
|
|
2063
2125
|
Returns:
|
|
2064
2126
|
ProjectStatus: Created project status.
|
|
2065
2127
|
|
|
2066
2128
|
"""
|
|
2067
2129
|
status = ProjectStatus(
|
|
2068
|
-
name, short_name, state, icon, color, is_new=True
|
|
2130
|
+
name, short_name, state, icon, color, scope, is_new=True
|
|
2069
2131
|
)
|
|
2070
2132
|
self.append(status)
|
|
2071
2133
|
return status
|
|
2072
2134
|
|
|
2135
|
+
def set_status_scope_supported(self, supported: bool):
|
|
2136
|
+
self._scope_supported = supported
|
|
2137
|
+
|
|
2073
2138
|
def lock(self):
|
|
2074
2139
|
"""Lock statuses.
|
|
2075
2140
|
|
|
@@ -2083,10 +2148,15 @@ class _ProjectStatuses:
|
|
|
2083
2148
|
|
|
2084
2149
|
def to_data(self):
|
|
2085
2150
|
"""Convert to project statuses data."""
|
|
2086
|
-
|
|
2151
|
+
output = [
|
|
2087
2152
|
status.to_data()
|
|
2088
2153
|
for status in self._statuses
|
|
2089
2154
|
]
|
|
2155
|
+
# Remove scope if is not supported
|
|
2156
|
+
if not self._scope_supported:
|
|
2157
|
+
for item in output:
|
|
2158
|
+
item.pop("scope")
|
|
2159
|
+
return output
|
|
2090
2160
|
|
|
2091
2161
|
def set(self, statuses):
|
|
2092
2162
|
"""Explicitly override statuses.
|
|
@@ -2348,9 +2418,9 @@ class ProjectEntity(BaseEntity):
|
|
|
2348
2418
|
task_types,
|
|
2349
2419
|
statuses,
|
|
2350
2420
|
*args,
|
|
2351
|
-
**kwargs
|
|
2421
|
+
**kwargs,
|
|
2352
2422
|
):
|
|
2353
|
-
super(
|
|
2423
|
+
super().__init__(*args, **kwargs)
|
|
2354
2424
|
|
|
2355
2425
|
self._project_code = project_code
|
|
2356
2426
|
self._library_project = library
|
|
@@ -2379,6 +2449,9 @@ class ProjectEntity(BaseEntity):
|
|
|
2379
2449
|
"Parent of project cannot be set to {}".format(parent)
|
|
2380
2450
|
)
|
|
2381
2451
|
|
|
2452
|
+
def set_status_scope_supported(self, supported: bool):
|
|
2453
|
+
self._statuses_obj.set_status_scope_supported(supported)
|
|
2454
|
+
|
|
2382
2455
|
parent = property(get_parent, set_parent)
|
|
2383
2456
|
|
|
2384
2457
|
def get_orig_folder_types(self):
|
|
@@ -2436,7 +2509,7 @@ class ProjectEntity(BaseEntity):
|
|
|
2436
2509
|
return self._statuses_obj.get_status_by_slugified_name(name)
|
|
2437
2510
|
|
|
2438
2511
|
def lock(self):
|
|
2439
|
-
super(
|
|
2512
|
+
super().lock()
|
|
2440
2513
|
self._orig_folder_types = copy.deepcopy(self._folder_types)
|
|
2441
2514
|
self._orig_task_types = copy.deepcopy(self._task_types)
|
|
2442
2515
|
self._statuses_obj.lock()
|
|
@@ -2469,7 +2542,7 @@ class ProjectEntity(BaseEntity):
|
|
|
2469
2542
|
attribs=project["ownAttrib"],
|
|
2470
2543
|
data=project["data"],
|
|
2471
2544
|
active=project["active"],
|
|
2472
|
-
entity_hub=entity_hub
|
|
2545
|
+
entity_hub=entity_hub,
|
|
2473
2546
|
)
|
|
2474
2547
|
|
|
2475
2548
|
def to_create_body_data(self):
|
|
@@ -2577,6 +2650,12 @@ class FolderEntity(BaseEntity):
|
|
|
2577
2650
|
raise ValueError(
|
|
2578
2651
|
f"Status {status_name} is not available on project."
|
|
2579
2652
|
)
|
|
2653
|
+
|
|
2654
|
+
if not status.is_available_for_entity_type("folder"):
|
|
2655
|
+
raise ValueError(
|
|
2656
|
+
f"Status {status_name} is not available for folder."
|
|
2657
|
+
)
|
|
2658
|
+
|
|
2580
2659
|
self._status = status_name
|
|
2581
2660
|
|
|
2582
2661
|
status = property(get_status, set_status)
|
|
@@ -2795,7 +2874,7 @@ class TaskEntity(BaseEntity):
|
|
|
2795
2874
|
tags = []
|
|
2796
2875
|
else:
|
|
2797
2876
|
tags = list(tags)
|
|
2798
|
-
|
|
2877
|
+
|
|
2799
2878
|
if assignees is None:
|
|
2800
2879
|
assignees = []
|
|
2801
2880
|
else:
|
|
@@ -2861,6 +2940,12 @@ class TaskEntity(BaseEntity):
|
|
|
2861
2940
|
raise ValueError(
|
|
2862
2941
|
f"Status {status_name} is not available on project."
|
|
2863
2942
|
)
|
|
2943
|
+
|
|
2944
|
+
if not status.is_available_for_entity_type("task"):
|
|
2945
|
+
raise ValueError(
|
|
2946
|
+
f"Status {status_name} is not available for task."
|
|
2947
|
+
)
|
|
2948
|
+
|
|
2864
2949
|
self._status = status_name
|
|
2865
2950
|
|
|
2866
2951
|
status = property(get_status, set_status)
|
|
@@ -44,7 +44,6 @@ class ServerEvent(object):
|
|
|
44
44
|
"user": self.username,
|
|
45
45
|
"dependencies": copy.deepcopy(self.dependencies),
|
|
46
46
|
"description": self.description,
|
|
47
|
-
"description": self.description,
|
|
48
47
|
"summary": copy.deepcopy(self.summary),
|
|
49
48
|
"payload": self.payload,
|
|
50
49
|
"finished": self.finished,
|
|
@@ -389,7 +389,9 @@ def representations_graphql_query(fields):
|
|
|
389
389
|
repre_ids_var = query.add_variable("representationIds", "[String!]")
|
|
390
390
|
repre_names_var = query.add_variable("representationNames", "[String!]")
|
|
391
391
|
version_ids_var = query.add_variable("versionIds", "[String!]")
|
|
392
|
-
has_links_var = query.add_variable(
|
|
392
|
+
has_links_var = query.add_variable(
|
|
393
|
+
"representationHasLinks", "HasLinksFilter"
|
|
394
|
+
)
|
|
393
395
|
statuses_var = query.add_variable(
|
|
394
396
|
"representationStatuses", "[String!]"
|
|
395
397
|
)
|
|
@@ -16,6 +16,7 @@ import uuid
|
|
|
16
16
|
import warnings
|
|
17
17
|
import itertools
|
|
18
18
|
from contextlib import contextmanager
|
|
19
|
+
from typing import Optional
|
|
19
20
|
|
|
20
21
|
try:
|
|
21
22
|
from http import HTTPStatus
|
|
@@ -1276,7 +1277,11 @@ class ServerAPI(object):
|
|
|
1276
1277
|
# Server may be restarting
|
|
1277
1278
|
new_response = RestApiResponse(
|
|
1278
1279
|
None,
|
|
1279
|
-
{
|
|
1280
|
+
{
|
|
1281
|
+
"detail": (
|
|
1282
|
+
"Unable to connect the server. Connection refused"
|
|
1283
|
+
)
|
|
1284
|
+
}
|
|
1280
1285
|
)
|
|
1281
1286
|
|
|
1282
1287
|
except requests.exceptions.Timeout:
|
|
@@ -1295,7 +1300,11 @@ class ServerAPI(object):
|
|
|
1295
1300
|
|
|
1296
1301
|
new_response = RestApiResponse(
|
|
1297
1302
|
None,
|
|
1298
|
-
{
|
|
1303
|
+
{
|
|
1304
|
+
"detail": (
|
|
1305
|
+
"Unable to connect the server. Connection error"
|
|
1306
|
+
)
|
|
1307
|
+
}
|
|
1299
1308
|
)
|
|
1300
1309
|
|
|
1301
1310
|
time.sleep(0.1)
|
|
@@ -1323,9 +1332,8 @@ class ServerAPI(object):
|
|
|
1323
1332
|
return new_response
|
|
1324
1333
|
|
|
1325
1334
|
def raw_post(self, entrypoint, **kwargs):
|
|
1326
|
-
|
|
1327
|
-
self.log.debug("Executing [POST] {}".format(
|
|
1328
|
-
url = "{}/{}".format(self._rest_url, entrypoint)
|
|
1335
|
+
url = self._endpoint_to_url(entrypoint)
|
|
1336
|
+
self.log.debug("Executing [POST] {}".format(url))
|
|
1329
1337
|
return self._do_rest_request(
|
|
1330
1338
|
RequestTypes.post,
|
|
1331
1339
|
url,
|
|
@@ -1333,9 +1341,8 @@ class ServerAPI(object):
|
|
|
1333
1341
|
)
|
|
1334
1342
|
|
|
1335
1343
|
def raw_put(self, entrypoint, **kwargs):
|
|
1336
|
-
|
|
1337
|
-
self.log.debug("Executing [PUT] {}".format(
|
|
1338
|
-
url = "{}/{}".format(self._rest_url, entrypoint)
|
|
1344
|
+
url = self._endpoint_to_url(entrypoint)
|
|
1345
|
+
self.log.debug("Executing [PUT] {}".format(url))
|
|
1339
1346
|
return self._do_rest_request(
|
|
1340
1347
|
RequestTypes.put,
|
|
1341
1348
|
url,
|
|
@@ -1343,9 +1350,8 @@ class ServerAPI(object):
|
|
|
1343
1350
|
)
|
|
1344
1351
|
|
|
1345
1352
|
def raw_patch(self, entrypoint, **kwargs):
|
|
1346
|
-
|
|
1347
|
-
self.log.debug("Executing [PATCH] {}".format(
|
|
1348
|
-
url = "{}/{}".format(self._rest_url, entrypoint)
|
|
1353
|
+
url = self._endpoint_to_url(entrypoint)
|
|
1354
|
+
self.log.debug("Executing [PATCH] {}".format(url))
|
|
1349
1355
|
return self._do_rest_request(
|
|
1350
1356
|
RequestTypes.patch,
|
|
1351
1357
|
url,
|
|
@@ -1353,9 +1359,8 @@ class ServerAPI(object):
|
|
|
1353
1359
|
)
|
|
1354
1360
|
|
|
1355
1361
|
def raw_get(self, entrypoint, **kwargs):
|
|
1356
|
-
|
|
1357
|
-
self.log.debug("Executing [GET] {}".format(
|
|
1358
|
-
url = "{}/{}".format(self._rest_url, entrypoint)
|
|
1362
|
+
url = self._endpoint_to_url(entrypoint)
|
|
1363
|
+
self.log.debug("Executing [GET] {}".format(url))
|
|
1359
1364
|
return self._do_rest_request(
|
|
1360
1365
|
RequestTypes.get,
|
|
1361
1366
|
url,
|
|
@@ -1363,9 +1368,8 @@ class ServerAPI(object):
|
|
|
1363
1368
|
)
|
|
1364
1369
|
|
|
1365
1370
|
def raw_delete(self, entrypoint, **kwargs):
|
|
1366
|
-
|
|
1367
|
-
self.log.debug("Executing [DELETE] {}".format(
|
|
1368
|
-
url = "{}/{}".format(self._rest_url, entrypoint)
|
|
1371
|
+
url = self._endpoint_to_url(entrypoint)
|
|
1372
|
+
self.log.debug("Executing [DELETE] {}".format(url))
|
|
1369
1373
|
return self._do_rest_request(
|
|
1370
1374
|
RequestTypes.delete,
|
|
1371
1375
|
url,
|
|
@@ -1569,8 +1573,8 @@ class ServerAPI(object):
|
|
|
1569
1573
|
depends_on (Optional[str]): Add dependency to another event.
|
|
1570
1574
|
username (Optional[str]): Username which triggered event.
|
|
1571
1575
|
description (Optional[str]): Description of event.
|
|
1572
|
-
summary (Optional[dict[str, Any]]): Summary of event that can
|
|
1573
|
-
for simple filtering on listeners.
|
|
1576
|
+
summary (Optional[dict[str, Any]]): Summary of event that can
|
|
1577
|
+
be used for simple filtering on listeners.
|
|
1574
1578
|
payload (Optional[dict[str, Any]]): Full payload of event data with
|
|
1575
1579
|
all details.
|
|
1576
1580
|
finished (Optional[bool]): Mark event as finished on dispatch.
|
|
@@ -1637,7 +1641,7 @@ class ServerAPI(object):
|
|
|
1637
1641
|
at least one unfinished event with target topic, when set to 'True'.
|
|
1638
1642
|
This helps when order of events matter and more than one process using
|
|
1639
1643
|
the same target is running at the same time.
|
|
1640
|
-
|
|
1644
|
+
|
|
1641
1645
|
Make sure the new event has updated status to '"finished"' status
|
|
1642
1646
|
when you're done with logic
|
|
1643
1647
|
|
|
@@ -1708,6 +1712,30 @@ class ServerAPI(object):
|
|
|
1708
1712
|
|
|
1709
1713
|
return response.data
|
|
1710
1714
|
|
|
1715
|
+
def _endpoint_to_url(
|
|
1716
|
+
self,
|
|
1717
|
+
endpoint: str,
|
|
1718
|
+
use_rest: Optional[bool] = True
|
|
1719
|
+
):
|
|
1720
|
+
"""Cleanup endpoint and return full url to AYON server.
|
|
1721
|
+
|
|
1722
|
+
If endpoint already starts with server url only slashes are removed.
|
|
1723
|
+
|
|
1724
|
+
Args:
|
|
1725
|
+
endpoint (str): Endpoint to be cleaned.
|
|
1726
|
+
use_rest (Optional[bool]): Use only base server url if set to
|
|
1727
|
+
False, otherwise REST endpoint is used.
|
|
1728
|
+
|
|
1729
|
+
Returns:
|
|
1730
|
+
str: Full url to AYON server.
|
|
1731
|
+
|
|
1732
|
+
"""
|
|
1733
|
+
endpoint = endpoint.lstrip("/").rstrip("/")
|
|
1734
|
+
if endpoint.startswith(self._base_url):
|
|
1735
|
+
return endpoint
|
|
1736
|
+
base_url = self._rest_url if use_rest else self._graphql_url
|
|
1737
|
+
return f"{base_url}/{endpoint}"
|
|
1738
|
+
|
|
1711
1739
|
def _download_file_to_stream(self, url, stream, chunk_size, progress):
|
|
1712
1740
|
kwargs = {"stream": True}
|
|
1713
1741
|
if self._session is None:
|
|
@@ -1740,7 +1768,8 @@ class ServerAPI(object):
|
|
|
1740
1768
|
|
|
1741
1769
|
Args:
|
|
1742
1770
|
endpoint (str): Endpoint or URL to file that should be downloaded.
|
|
1743
|
-
stream (Union[io.BytesIO, BinaryIO]): Stream where output will
|
|
1771
|
+
stream (Union[io.BytesIO, BinaryIO]): Stream where output will
|
|
1772
|
+
be stored.
|
|
1744
1773
|
chunk_size (Optional[int]): Size of chunks that are received
|
|
1745
1774
|
in single loop.
|
|
1746
1775
|
progress (Optional[TransferProgress]): Object that gives ability
|
|
@@ -1752,9 +1781,8 @@ class ServerAPI(object):
|
|
|
1752
1781
|
|
|
1753
1782
|
if endpoint.startswith(self._base_url):
|
|
1754
1783
|
url = endpoint
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
url = "{}/{}".format(self._rest_url, endpoint)
|
|
1784
|
+
|
|
1785
|
+
url = self._endpoint_to_url(endpoint)
|
|
1758
1786
|
|
|
1759
1787
|
if progress is None:
|
|
1760
1788
|
progress = TransferProgress()
|
|
@@ -1832,7 +1860,7 @@ class ServerAPI(object):
|
|
|
1832
1860
|
|
|
1833
1861
|
Yields:
|
|
1834
1862
|
bytes: Chunk of file.
|
|
1835
|
-
|
|
1863
|
+
|
|
1836
1864
|
"""
|
|
1837
1865
|
# Get size of file
|
|
1838
1866
|
file_stream.seek(0, io.SEEK_END)
|
|
@@ -1922,11 +1950,7 @@ class ServerAPI(object):
|
|
|
1922
1950
|
requests.Response: Response object
|
|
1923
1951
|
|
|
1924
1952
|
"""
|
|
1925
|
-
|
|
1926
|
-
url = endpoint
|
|
1927
|
-
else:
|
|
1928
|
-
endpoint = endpoint.lstrip("/").rstrip("/")
|
|
1929
|
-
url = "{}/{}".format(self._rest_url, endpoint)
|
|
1953
|
+
url = self._endpoint_to_url(endpoint)
|
|
1930
1954
|
|
|
1931
1955
|
# Create dummy object so the function does not have to check
|
|
1932
1956
|
# 'progress' variable everywhere
|
|
@@ -1969,7 +1993,7 @@ class ServerAPI(object):
|
|
|
1969
1993
|
|
|
1970
1994
|
Returns:
|
|
1971
1995
|
requests.Response: Response object
|
|
1972
|
-
|
|
1996
|
+
|
|
1973
1997
|
"""
|
|
1974
1998
|
if progress is None:
|
|
1975
1999
|
progress = TransferProgress()
|
|
@@ -2189,7 +2213,7 @@ class ServerAPI(object):
|
|
|
2189
2213
|
"""Get attribute schemas available for an entity type.
|
|
2190
2214
|
|
|
2191
2215
|
Example::
|
|
2192
|
-
|
|
2216
|
+
|
|
2193
2217
|
```
|
|
2194
2218
|
# Example attribute schema
|
|
2195
2219
|
{
|
|
@@ -2337,15 +2361,15 @@ class ServerAPI(object):
|
|
|
2337
2361
|
response.raise_for_status()
|
|
2338
2362
|
return response.data
|
|
2339
2363
|
|
|
2340
|
-
def
|
|
2341
|
-
"""Calculate
|
|
2364
|
+
def get_addon_endpoint(self, addon_name, addon_version, *subpaths):
|
|
2365
|
+
"""Calculate endpoint to addon route.
|
|
2342
2366
|
|
|
2343
2367
|
Examples:
|
|
2344
2368
|
|
|
2345
2369
|
>>> api = ServerAPI("https://your.url.com")
|
|
2346
2370
|
>>> api.get_addon_url(
|
|
2347
2371
|
... "example", "1.0.0", "private", "my.zip")
|
|
2348
|
-
'
|
|
2372
|
+
'addons/example/1.0.0/private/my.zip'
|
|
2349
2373
|
|
|
2350
2374
|
Args:
|
|
2351
2375
|
addon_name (str): Name of addon.
|
|
@@ -2360,13 +2384,41 @@ class ServerAPI(object):
|
|
|
2360
2384
|
ending = ""
|
|
2361
2385
|
if subpaths:
|
|
2362
2386
|
ending = "/{}".format("/".join(subpaths))
|
|
2363
|
-
return "
|
|
2364
|
-
self._base_url,
|
|
2387
|
+
return "addons/{}/{}{}".format(
|
|
2365
2388
|
addon_name,
|
|
2366
2389
|
addon_version,
|
|
2367
2390
|
ending
|
|
2368
2391
|
)
|
|
2369
2392
|
|
|
2393
|
+
def get_addon_url(
|
|
2394
|
+
self, addon_name, addon_version, *subpaths, use_rest=True
|
|
2395
|
+
):
|
|
2396
|
+
"""Calculate url to addon route.
|
|
2397
|
+
|
|
2398
|
+
Examples:
|
|
2399
|
+
|
|
2400
|
+
>>> api = ServerAPI("https://your.url.com")
|
|
2401
|
+
>>> api.get_addon_url(
|
|
2402
|
+
... "example", "1.0.0", "private", "my.zip")
|
|
2403
|
+
'https://your.url.com/api/addons/example/1.0.0/private/my.zip'
|
|
2404
|
+
|
|
2405
|
+
Args:
|
|
2406
|
+
addon_name (str): Name of addon.
|
|
2407
|
+
addon_version (str): Version of addon.
|
|
2408
|
+
*subpaths (str): Any amount of subpaths that are added to
|
|
2409
|
+
addon url.
|
|
2410
|
+
use_rest (Optional[bool]): Use rest endpoint.
|
|
2411
|
+
|
|
2412
|
+
Returns:
|
|
2413
|
+
str: Final url.
|
|
2414
|
+
|
|
2415
|
+
"""
|
|
2416
|
+
endpoint = self.get_addon_endpoint(
|
|
2417
|
+
addon_name, addon_version, *subpaths
|
|
2418
|
+
)
|
|
2419
|
+
url_base = self._base_url if use_rest else self._rest_url
|
|
2420
|
+
return f"{url_base}/{endpoint}"
|
|
2421
|
+
|
|
2370
2422
|
def download_addon_private_file(
|
|
2371
2423
|
self,
|
|
2372
2424
|
addon_name,
|
|
@@ -2404,12 +2456,13 @@ class ServerAPI(object):
|
|
|
2404
2456
|
dst_dirpath = os.path.dirname(dst_filepath)
|
|
2405
2457
|
os.makedirs(dst_dirpath, exist_ok=True)
|
|
2406
2458
|
|
|
2407
|
-
|
|
2459
|
+
endpoint = self.get_addon_endpoint(
|
|
2408
2460
|
addon_name,
|
|
2409
2461
|
addon_version,
|
|
2410
2462
|
"private",
|
|
2411
2463
|
filename
|
|
2412
2464
|
)
|
|
2465
|
+
url = f"{self._base_url}/{endpoint}"
|
|
2413
2466
|
self.download_file(
|
|
2414
2467
|
url, dst_filepath, chunk_size=chunk_size, progress=progress
|
|
2415
2468
|
)
|
|
@@ -2811,7 +2864,7 @@ class ServerAPI(object):
|
|
|
2811
2864
|
"""Server bundles with basic information.
|
|
2812
2865
|
|
|
2813
2866
|
This is example output::
|
|
2814
|
-
|
|
2867
|
+
|
|
2815
2868
|
{
|
|
2816
2869
|
"bundles": [
|
|
2817
2870
|
{
|
|
@@ -2849,12 +2902,31 @@ class ServerAPI(object):
|
|
|
2849
2902
|
installer_version,
|
|
2850
2903
|
dependency_packages=None,
|
|
2851
2904
|
is_production=None,
|
|
2852
|
-
is_staging=None
|
|
2905
|
+
is_staging=None,
|
|
2906
|
+
is_dev=None,
|
|
2907
|
+
dev_active_user=None,
|
|
2908
|
+
dev_addons_config=None,
|
|
2853
2909
|
):
|
|
2854
2910
|
"""Create bundle on server.
|
|
2855
2911
|
|
|
2856
2912
|
Bundle cannot be changed once is created. Only isProduction, isStaging
|
|
2857
|
-
and dependency packages can change after creation.
|
|
2913
|
+
and dependency packages can change after creation. In case dev bundle
|
|
2914
|
+
is created, it is possible to change anything, but it is not possible
|
|
2915
|
+
to mark bundle as dev and production or staging at the same time.
|
|
2916
|
+
|
|
2917
|
+
Development addon config can define custom path to client code. It is
|
|
2918
|
+
used only for dev bundles.
|
|
2919
|
+
|
|
2920
|
+
Example of 'dev_addons_config'::
|
|
2921
|
+
|
|
2922
|
+
```json
|
|
2923
|
+
{
|
|
2924
|
+
"core": {
|
|
2925
|
+
"enabled": true,
|
|
2926
|
+
"path": "/path/to/ayon-core/client"
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
```
|
|
2858
2930
|
|
|
2859
2931
|
Args:
|
|
2860
2932
|
name (str): Name of bundle.
|
|
@@ -2866,6 +2938,11 @@ class ServerAPI(object):
|
|
|
2866
2938
|
is_production (Optional[bool]): Bundle will be marked as
|
|
2867
2939
|
production.
|
|
2868
2940
|
is_staging (Optional[bool]): Bundle will be marked as staging.
|
|
2941
|
+
is_dev (Optional[bool]): Bundle will be marked as dev.
|
|
2942
|
+
dev_active_user (Optional[str]): Username that will be assigned
|
|
2943
|
+
to dev bundle. Can be used only if 'is_dev' is set to 'True'.
|
|
2944
|
+
dev_addons_config (Optional[dict[str, Any]]): Configuration for
|
|
2945
|
+
dev addons. Can be used only if 'is_dev' is set to 'True'.
|
|
2869
2946
|
|
|
2870
2947
|
"""
|
|
2871
2948
|
body = {
|
|
@@ -2873,10 +2950,14 @@ class ServerAPI(object):
|
|
|
2873
2950
|
"installerVersion": installer_version,
|
|
2874
2951
|
"addons": addon_versions,
|
|
2875
2952
|
}
|
|
2953
|
+
|
|
2876
2954
|
for key, value in (
|
|
2877
2955
|
("dependencyPackages", dependency_packages),
|
|
2878
2956
|
("isProduction", is_production),
|
|
2879
2957
|
("isStaging", is_staging),
|
|
2958
|
+
("isDev", is_dev),
|
|
2959
|
+
("activeUser", dev_active_user),
|
|
2960
|
+
("addonDevelopment", dev_addons_config),
|
|
2880
2961
|
):
|
|
2881
2962
|
if value is not None:
|
|
2882
2963
|
body[key] = value
|
|
@@ -2887,9 +2968,14 @@ class ServerAPI(object):
|
|
|
2887
2968
|
def update_bundle(
|
|
2888
2969
|
self,
|
|
2889
2970
|
bundle_name,
|
|
2971
|
+
addon_versions=None,
|
|
2972
|
+
installer_version=None,
|
|
2890
2973
|
dependency_packages=None,
|
|
2891
2974
|
is_production=None,
|
|
2892
|
-
is_staging=None
|
|
2975
|
+
is_staging=None,
|
|
2976
|
+
is_dev=None,
|
|
2977
|
+
dev_active_user=None,
|
|
2978
|
+
dev_addons_config=None,
|
|
2893
2979
|
):
|
|
2894
2980
|
"""Update bundle on server.
|
|
2895
2981
|
|
|
@@ -2899,19 +2985,33 @@ class ServerAPI(object):
|
|
|
2899
2985
|
|
|
2900
2986
|
Args:
|
|
2901
2987
|
bundle_name (str): Name of bundle.
|
|
2988
|
+
addon_versions (Optional[dict[str, str]]): Addon versions,
|
|
2989
|
+
possible only for dev bundles.
|
|
2990
|
+
installer_version (Optional[str]): Installer version, possible
|
|
2991
|
+
only for dev bundles.
|
|
2902
2992
|
dependency_packages (Optional[dict[str, str]]): Dependency pacakge
|
|
2903
2993
|
names that should be used with the bundle.
|
|
2904
2994
|
is_production (Optional[bool]): Bundle will be marked as
|
|
2905
2995
|
production.
|
|
2906
2996
|
is_staging (Optional[bool]): Bundle will be marked as staging.
|
|
2997
|
+
is_dev (Optional[bool]): Bundle will be marked as dev.
|
|
2998
|
+
dev_active_user (Optional[str]): Username that will be assigned
|
|
2999
|
+
to dev bundle. Can be used only for dev bundles.
|
|
3000
|
+
dev_addons_config (Optional[dict[str, Any]]): Configuration for
|
|
3001
|
+
dev addons. Can be used only for dev bundles.
|
|
2907
3002
|
|
|
2908
3003
|
"""
|
|
2909
3004
|
body = {
|
|
2910
3005
|
key: value
|
|
2911
3006
|
for key, value in (
|
|
3007
|
+
("installerVersion", installer_version),
|
|
3008
|
+
("addons", addon_versions),
|
|
2912
3009
|
("dependencyPackages", dependency_packages),
|
|
2913
3010
|
("isProduction", is_production),
|
|
2914
3011
|
("isStaging", is_staging),
|
|
3012
|
+
("isDev", is_dev),
|
|
3013
|
+
("activeUser", dev_active_user),
|
|
3014
|
+
("addonDevelopment", dev_addons_config),
|
|
2915
3015
|
)
|
|
2916
3016
|
if value is not None
|
|
2917
3017
|
}
|
|
@@ -2921,6 +3021,63 @@ class ServerAPI(object):
|
|
|
2921
3021
|
)
|
|
2922
3022
|
response.raise_for_status()
|
|
2923
3023
|
|
|
3024
|
+
def check_bundle_compatibility(
|
|
3025
|
+
self,
|
|
3026
|
+
name,
|
|
3027
|
+
addon_versions,
|
|
3028
|
+
installer_version,
|
|
3029
|
+
dependency_packages=None,
|
|
3030
|
+
is_production=None,
|
|
3031
|
+
is_staging=None,
|
|
3032
|
+
is_dev=None,
|
|
3033
|
+
dev_active_user=None,
|
|
3034
|
+
dev_addons_config=None,
|
|
3035
|
+
):
|
|
3036
|
+
"""Check bundle compatibility.
|
|
3037
|
+
|
|
3038
|
+
Can be used as per-flight validation before creating bundle.
|
|
3039
|
+
|
|
3040
|
+
Args:
|
|
3041
|
+
name (str): Name of bundle.
|
|
3042
|
+
addon_versions (dict[str, str]): Addon versions.
|
|
3043
|
+
installer_version (Union[str, None]): Installer version.
|
|
3044
|
+
dependency_packages (Optional[dict[str, str]]): Dependency
|
|
3045
|
+
package names. Keys are platform names and values are name of
|
|
3046
|
+
packages.
|
|
3047
|
+
is_production (Optional[bool]): Bundle will be marked as
|
|
3048
|
+
production.
|
|
3049
|
+
is_staging (Optional[bool]): Bundle will be marked as staging.
|
|
3050
|
+
is_dev (Optional[bool]): Bundle will be marked as dev.
|
|
3051
|
+
dev_active_user (Optional[str]): Username that will be assigned
|
|
3052
|
+
to dev bundle. Can be used only if 'is_dev' is set to 'True'.
|
|
3053
|
+
dev_addons_config (Optional[dict[str, Any]]): Configuration for
|
|
3054
|
+
dev addons. Can be used only if 'is_dev' is set to 'True'.
|
|
3055
|
+
|
|
3056
|
+
Returns:
|
|
3057
|
+
Dict[str, Any]: Server response, with 'success' and 'issues'.
|
|
3058
|
+
|
|
3059
|
+
"""
|
|
3060
|
+
body = {
|
|
3061
|
+
"name": name,
|
|
3062
|
+
"installerVersion": installer_version,
|
|
3063
|
+
"addons": addon_versions,
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
for key, value in (
|
|
3067
|
+
("dependencyPackages", dependency_packages),
|
|
3068
|
+
("isProduction", is_production),
|
|
3069
|
+
("isStaging", is_staging),
|
|
3070
|
+
("isDev", is_dev),
|
|
3071
|
+
("activeUser", dev_active_user),
|
|
3072
|
+
("addonDevelopment", dev_addons_config),
|
|
3073
|
+
):
|
|
3074
|
+
if value is not None:
|
|
3075
|
+
body[key] = value
|
|
3076
|
+
|
|
3077
|
+
response = self.post("bundles/check", **body)
|
|
3078
|
+
response.raise_for_status()
|
|
3079
|
+
return response.data
|
|
3080
|
+
|
|
2924
3081
|
def delete_bundle(self, bundle_name):
|
|
2925
3082
|
"""Delete bundle from server.
|
|
2926
3083
|
|
|
@@ -3222,7 +3379,7 @@ class ServerAPI(object):
|
|
|
3222
3379
|
if project_name:
|
|
3223
3380
|
args = (project_name, )
|
|
3224
3381
|
|
|
3225
|
-
endpoint = self.
|
|
3382
|
+
endpoint = self.get_addon_endpoint(
|
|
3226
3383
|
addon_name, addon_version, "schema", *args
|
|
3227
3384
|
)
|
|
3228
3385
|
result = self.get(endpoint)
|
|
@@ -3726,9 +3883,23 @@ class ServerAPI(object):
|
|
|
3726
3883
|
return None
|
|
3727
3884
|
|
|
3728
3885
|
response = self.get("projects/{}".format(project_name))
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3886
|
+
# TODO ignore only error about not existing project
|
|
3887
|
+
if response.status != 200:
|
|
3888
|
+
return None
|
|
3889
|
+
project = response.data
|
|
3890
|
+
# Add fake scope to statuses if not available
|
|
3891
|
+
for status in project["statuses"]:
|
|
3892
|
+
scope = status.get("scope")
|
|
3893
|
+
if scope is None:
|
|
3894
|
+
status["scope"] = [
|
|
3895
|
+
"folder",
|
|
3896
|
+
"task",
|
|
3897
|
+
"product",
|
|
3898
|
+
"version",
|
|
3899
|
+
"representation",
|
|
3900
|
+
"workfile"
|
|
3901
|
+
]
|
|
3902
|
+
return project
|
|
3732
3903
|
|
|
3733
3904
|
def get_rest_projects(self, active=True, library=None):
|
|
3734
3905
|
"""Query available project entities.
|
|
@@ -4144,8 +4315,8 @@ class ServerAPI(object):
|
|
|
4144
4315
|
"""Query folders from server.
|
|
4145
4316
|
|
|
4146
4317
|
Todos:
|
|
4147
|
-
Folder name won't be unique identifier, so we should add
|
|
4148
|
-
filtering.
|
|
4318
|
+
Folder name won't be unique identifier, so we should add
|
|
4319
|
+
folder path filtering.
|
|
4149
4320
|
|
|
4150
4321
|
Notes:
|
|
4151
4322
|
Filter 'active' don't have direct filter in GraphQl.
|
|
@@ -6504,6 +6675,8 @@ class ServerAPI(object):
|
|
|
6504
6675
|
product = version.pop("product", {})
|
|
6505
6676
|
task = version.pop("task", None)
|
|
6506
6677
|
folder = product.pop("folder", {})
|
|
6678
|
+
self._convert_entity_data(repre)
|
|
6679
|
+
self._representation_conversion(repre)
|
|
6507
6680
|
self._convert_entity_data(version)
|
|
6508
6681
|
self._convert_entity_data(product)
|
|
6509
6682
|
self._convert_entity_data(folder)
|
|
@@ -6609,7 +6782,7 @@ class ServerAPI(object):
|
|
|
6609
6782
|
|
|
6610
6783
|
def get_representation_parents(
|
|
6611
6784
|
self,
|
|
6612
|
-
project_name,
|
|
6785
|
+
project_name,
|
|
6613
6786
|
representation_id,
|
|
6614
6787
|
project_fields=None,
|
|
6615
6788
|
folder_fields=None,
|
|
@@ -6945,7 +7118,7 @@ class ServerAPI(object):
|
|
|
6945
7118
|
"attrib.{}".format(attr)
|
|
6946
7119
|
for attr in self.get_attributes_for_type("workfile")
|
|
6947
7120
|
}
|
|
6948
|
-
|
|
7121
|
+
|
|
6949
7122
|
if own_attributes is not _PLACEHOLDER:
|
|
6950
7123
|
warnings.warn(
|
|
6951
7124
|
(
|
|
@@ -6963,6 +7136,7 @@ class ServerAPI(object):
|
|
|
6963
7136
|
|
|
6964
7137
|
for parsed_data in query.continuous_query(self):
|
|
6965
7138
|
for workfile_info in parsed_data["project"]["workfiles"]:
|
|
7139
|
+
self._convert_entity_data(workfile_info)
|
|
6966
7140
|
yield workfile_info
|
|
6967
7141
|
|
|
6968
7142
|
def get_workfile_info(
|
|
@@ -8122,7 +8296,7 @@ class ServerAPI(object):
|
|
|
8122
8296
|
body = json.loads(
|
|
8123
8297
|
json.dumps(operation, default=entity_data_json_default)
|
|
8124
8298
|
)
|
|
8125
|
-
except:
|
|
8299
|
+
except (TypeError, ValueError):
|
|
8126
8300
|
raise ValueError("Couldn't json parse body: {}".format(
|
|
8127
8301
|
json.dumps(
|
|
8128
8302
|
operation, indent=4, default=failed_json_default
|
|
@@ -6,7 +6,7 @@ import string
|
|
|
6
6
|
import platform
|
|
7
7
|
import collections
|
|
8
8
|
from urllib.parse import urlparse, urlencode
|
|
9
|
-
from typing import Optional
|
|
9
|
+
from typing import Optional, Dict, Any
|
|
10
10
|
|
|
11
11
|
import requests
|
|
12
12
|
import unidecode
|
|
@@ -837,3 +837,52 @@ def get_media_mime_type(filepath: str) -> Optional[str]:
|
|
|
837
837
|
content = stream.read()
|
|
838
838
|
|
|
839
839
|
return get_media_mime_type_for_content(content)
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
def take_web_action_event(
|
|
843
|
+
server_url: str,
|
|
844
|
+
action_token: str
|
|
845
|
+
) -> Dict[str, Any]:
|
|
846
|
+
"""Take web action event using action token.
|
|
847
|
+
|
|
848
|
+
Action token is generated by AYON server and passed to AYON launcher.
|
|
849
|
+
|
|
850
|
+
Args:
|
|
851
|
+
server_url (str): AYON server url.
|
|
852
|
+
action_token (str): Action token.
|
|
853
|
+
|
|
854
|
+
Returns:
|
|
855
|
+
Dict[str, Any]: Web action event.
|
|
856
|
+
|
|
857
|
+
"""
|
|
858
|
+
response = requests.get(
|
|
859
|
+
f"{server_url}/api/actions/take/{action_token}"
|
|
860
|
+
)
|
|
861
|
+
response.raise_for_status()
|
|
862
|
+
return response.json()
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
def abort_web_action_event(
|
|
866
|
+
server_url: str,
|
|
867
|
+
action_token: str,
|
|
868
|
+
reason: str
|
|
869
|
+
):
|
|
870
|
+
"""Abort web action event using action token.
|
|
871
|
+
|
|
872
|
+
A web action event could not be processed for some reason.
|
|
873
|
+
|
|
874
|
+
Args:
|
|
875
|
+
server_url (str): AYON server url.
|
|
876
|
+
action_token (str): Action token.
|
|
877
|
+
reason (str): Reason why webaction event was aborted.
|
|
878
|
+
|
|
879
|
+
Returns:
|
|
880
|
+
requests.Response: Response from server.
|
|
881
|
+
|
|
882
|
+
"""
|
|
883
|
+
response = requests.post(
|
|
884
|
+
f"{server_url}/api/actions/take/{action_token}",
|
|
885
|
+
json={"message": reason},
|
|
886
|
+
)
|
|
887
|
+
response.raise_for_status()
|
|
888
|
+
return response
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""Package declaring Python API for AYON server."""
|
|
2
|
-
__version__ = "1.0.
|
|
2
|
+
__version__ = "1.0.10"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ayon-python-api"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.10"
|
|
4
4
|
description = "AYON Python API"
|
|
5
5
|
license = {file = "LICENSE"}
|
|
6
6
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
@@ -29,11 +29,14 @@ build-backend = "poetry.core.masonry.api"
|
|
|
29
29
|
|
|
30
30
|
[tool.poetry]
|
|
31
31
|
name = "ayon-python-api"
|
|
32
|
-
version = "1.0.
|
|
32
|
+
version = "1.0.10"
|
|
33
33
|
description = "AYON Python API"
|
|
34
34
|
authors = [
|
|
35
35
|
"ynput.io <info@ynput.io>"
|
|
36
36
|
]
|
|
37
|
+
packages = [
|
|
38
|
+
{ include = "ayon_api" }
|
|
39
|
+
]
|
|
37
40
|
|
|
38
41
|
[tool.poetry.dependencies]
|
|
39
42
|
python = ">=3.6.5"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ayon-python-api-1.0.9 → ayon-python-api-1.0.10}/ayon_python_api.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|