Ayon-Python-Api 0.1.3__tar.gz → 0.1.6__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-0.1.3/ayon_python_api.egg-info → ayon-python-api-0.1.6}/PKG-INFO +1 -1
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/__init__.py +70 -3
- ayon-python-api-0.1.6/ayon_api/exceptions.py +93 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/graphql.py +2 -33
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/server.py +242 -56
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/server_api.py +120 -10
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/utils.py +2 -16
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/version.py +1 -1
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6/ayon_python_api.egg-info}/PKG-INFO +1 -1
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/pyproject.toml +1 -1
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/setup.py +1 -1
- ayon-python-api-0.1.3/ayon_api/exceptions.py +0 -2
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/LICENSE +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/README.md +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/constants.py +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/entity_hub.py +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/events.py +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/graphql_queries.py +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/operations.py +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_api/thumbnails.py +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_python_api.egg-info/SOURCES.txt +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_python_api.egg-info/dependency_links.txt +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_python_api.egg-info/requires.txt +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_python_api.egg-info/top_level.txt +0 -0
- {ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/setup.cfg +0 -0
|
@@ -7,12 +7,47 @@ from .server_api import (
|
|
|
7
7
|
|
|
8
8
|
get_server_api_connection,
|
|
9
9
|
|
|
10
|
+
raw_get,
|
|
11
|
+
raw_post,
|
|
12
|
+
raw_put,
|
|
13
|
+
raw_patch,
|
|
14
|
+
raw_delete,
|
|
15
|
+
|
|
10
16
|
get,
|
|
11
17
|
post,
|
|
12
18
|
put,
|
|
13
19
|
patch,
|
|
14
20
|
delete,
|
|
15
21
|
|
|
22
|
+
download_file,
|
|
23
|
+
upload_file,
|
|
24
|
+
|
|
25
|
+
query_graphql,
|
|
26
|
+
|
|
27
|
+
get_addons_info,
|
|
28
|
+
|
|
29
|
+
get_dependencies_info,
|
|
30
|
+
update_dependency_info,
|
|
31
|
+
|
|
32
|
+
download_dependency_package,
|
|
33
|
+
upload_dependency_package,
|
|
34
|
+
delete_dependency_package,
|
|
35
|
+
|
|
36
|
+
get_full_production_settings,
|
|
37
|
+
get_production_settings,
|
|
38
|
+
get_full_project_settings,
|
|
39
|
+
get_project_settings,
|
|
40
|
+
get_addon_settings,
|
|
41
|
+
|
|
42
|
+
get_user,
|
|
43
|
+
get_users,
|
|
44
|
+
|
|
45
|
+
get_attributes_for_type,
|
|
46
|
+
get_default_fields_for_type,
|
|
47
|
+
|
|
48
|
+
get_project_anatomy_preset,
|
|
49
|
+
get_project_anatomy_presets,
|
|
50
|
+
|
|
16
51
|
get_projects,
|
|
17
52
|
get_project,
|
|
18
53
|
create_project,
|
|
@@ -47,9 +82,6 @@ from .server_api import (
|
|
|
47
82
|
get_representations_parents,
|
|
48
83
|
get_representation_parents,
|
|
49
84
|
|
|
50
|
-
get_project_settings,
|
|
51
|
-
get_addon_settings,
|
|
52
|
-
|
|
53
85
|
create_thumbnail,
|
|
54
86
|
get_thumbnail,
|
|
55
87
|
get_folder_thumbnail,
|
|
@@ -62,16 +94,50 @@ __all__ = (
|
|
|
62
94
|
"ServerAPIBase",
|
|
63
95
|
|
|
64
96
|
"ServerAPI",
|
|
97
|
+
|
|
65
98
|
"get_server_api_connection",
|
|
99
|
+
|
|
100
|
+
"raw_get",
|
|
101
|
+
"raw_post",
|
|
102
|
+
"raw_put",
|
|
103
|
+
"raw_patch",
|
|
104
|
+
"raw_delete",
|
|
105
|
+
|
|
66
106
|
"get",
|
|
67
107
|
"post",
|
|
68
108
|
"put",
|
|
69
109
|
"patch",
|
|
70
110
|
"delete",
|
|
71
111
|
|
|
112
|
+
"download_file",
|
|
113
|
+
"upload_file",
|
|
114
|
+
|
|
115
|
+
"query_graphql",
|
|
116
|
+
|
|
117
|
+
"get_addons_info",
|
|
118
|
+
|
|
119
|
+
"get_dependencies_info",
|
|
120
|
+
"update_dependency_info",
|
|
121
|
+
|
|
122
|
+
"download_dependency_package",
|
|
123
|
+
"upload_dependency_package",
|
|
124
|
+
"delete_dependency_package",
|
|
125
|
+
|
|
126
|
+
"get_full_production_settings",
|
|
127
|
+
"get_production_settings",
|
|
128
|
+
"get_full_project_settings",
|
|
72
129
|
"get_project_settings",
|
|
73
130
|
"get_addon_settings",
|
|
74
131
|
|
|
132
|
+
"get_user",
|
|
133
|
+
"get_users",
|
|
134
|
+
|
|
135
|
+
"get_attributes_for_type",
|
|
136
|
+
"get_default_fields_for_type",
|
|
137
|
+
|
|
138
|
+
"get_project_anatomy_preset",
|
|
139
|
+
"get_project_anatomy_presets",
|
|
140
|
+
|
|
75
141
|
"get_projects",
|
|
76
142
|
"get_project",
|
|
77
143
|
"create_project",
|
|
@@ -89,6 +155,7 @@ __all__ = (
|
|
|
89
155
|
"get_subset_by_name",
|
|
90
156
|
"get_subsets",
|
|
91
157
|
"get_subset_families",
|
|
158
|
+
|
|
92
159
|
"get_version_by_id",
|
|
93
160
|
"get_version_by_name",
|
|
94
161
|
"version_is_latest",
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class UrlError(Exception):
|
|
5
|
+
"""Url cannot be parsed as url.
|
|
6
|
+
|
|
7
|
+
Exception may contain hints of possible fixes of url that can be used in
|
|
8
|
+
UI if needed.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, message, title, hints=None):
|
|
12
|
+
if hints is None:
|
|
13
|
+
hints = []
|
|
14
|
+
|
|
15
|
+
self.title = title
|
|
16
|
+
self.hints = hints
|
|
17
|
+
super(UrlError, self).__init__(message)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ServerError(Exception):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class UnauthorizedError(ServerError):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AuthenticationError(ServerError):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ServerNotReached(ServerError):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class GraphQlQueryFailed(Exception):
|
|
37
|
+
def __init__(self, errors, query, variables):
|
|
38
|
+
if variables is None:
|
|
39
|
+
variables = {}
|
|
40
|
+
|
|
41
|
+
error_messages = []
|
|
42
|
+
for error in errors:
|
|
43
|
+
msg = error["message"]
|
|
44
|
+
path = error.get("path")
|
|
45
|
+
if path:
|
|
46
|
+
msg += " on item '{}'".format("/".join(path))
|
|
47
|
+
locations = error.get("locations")
|
|
48
|
+
if locations:
|
|
49
|
+
_locations = [
|
|
50
|
+
"Line {} Column {}".format(
|
|
51
|
+
location["line"], location["column"]
|
|
52
|
+
)
|
|
53
|
+
for location in locations
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
msg += " ({})".format(" and ".join(_locations))
|
|
57
|
+
error_messages.append(msg)
|
|
58
|
+
|
|
59
|
+
message = "GraphQl query Failed"
|
|
60
|
+
if error_messages:
|
|
61
|
+
message = "{}: {}".format(message, " | ".join(error_messages))
|
|
62
|
+
|
|
63
|
+
self.errors = errors
|
|
64
|
+
self.query = query
|
|
65
|
+
self.variables = copy.deepcopy(variables)
|
|
66
|
+
super(GraphQlQueryFailed, self).__init__(message)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class MissingEntityError(Exception):
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ProjectNotFound(MissingEntityError):
|
|
74
|
+
def __init__(self, project_name, message=None):
|
|
75
|
+
if not message:
|
|
76
|
+
message = "Project \"{}\" was not found".format(project_name)
|
|
77
|
+
self.project_name = project_name
|
|
78
|
+
super(ProjectNotFound, self).__init__(message)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class FolderNotFound(MissingEntityError):
|
|
82
|
+
def __init__(self, project_name, folder_id, message=None):
|
|
83
|
+
self.project_name = project_name
|
|
84
|
+
self.folder_id = folder_id
|
|
85
|
+
if not message:
|
|
86
|
+
message = (
|
|
87
|
+
"Folder with id \"{}\" was not found in project \"{}\""
|
|
88
|
+
).format(folder_id, project_name)
|
|
89
|
+
super(FolderNotFound, self).__init__(message)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class FailedOperations(Exception):
|
|
93
|
+
pass
|
|
@@ -4,6 +4,8 @@ from abc import ABCMeta, abstractproperty, abstractmethod
|
|
|
4
4
|
|
|
5
5
|
import six
|
|
6
6
|
|
|
7
|
+
from .exceptions import GraphQlQueryFailed
|
|
8
|
+
|
|
7
9
|
FIELD_VALUE = object()
|
|
8
10
|
|
|
9
11
|
|
|
@@ -29,39 +31,6 @@ def fields_to_dict(fields):
|
|
|
29
31
|
return output
|
|
30
32
|
|
|
31
33
|
|
|
32
|
-
class GraphQlQueryFailed(Exception):
|
|
33
|
-
def __init__(self, errors, query, variables):
|
|
34
|
-
if variables is None:
|
|
35
|
-
variables = {}
|
|
36
|
-
|
|
37
|
-
error_messages = []
|
|
38
|
-
for error in errors:
|
|
39
|
-
msg = error["message"]
|
|
40
|
-
path = error.get("path")
|
|
41
|
-
if path:
|
|
42
|
-
msg += " on item '{}'".format("/".join(path))
|
|
43
|
-
locations = error.get("locations")
|
|
44
|
-
if locations:
|
|
45
|
-
_locations = [
|
|
46
|
-
"Line {} Column {}".format(
|
|
47
|
-
location["line"], location["column"]
|
|
48
|
-
)
|
|
49
|
-
for location in locations
|
|
50
|
-
]
|
|
51
|
-
|
|
52
|
-
msg += " ({})".format(" and ".join(_locations))
|
|
53
|
-
error_messages.append(msg)
|
|
54
|
-
|
|
55
|
-
message = "GraphQl query Failed"
|
|
56
|
-
if error_messages:
|
|
57
|
-
message = "{}: {}".format(message, " | ".join(error_messages))
|
|
58
|
-
|
|
59
|
-
self.errors = errors
|
|
60
|
-
self.query = query
|
|
61
|
-
self.variables = copy.deepcopy(variables)
|
|
62
|
-
super(GraphQlQueryFailed, self).__init__(message)
|
|
63
|
-
|
|
64
|
-
|
|
65
34
|
class QueryVariable(object):
|
|
66
35
|
"""Object representing single varible used in GraphQlQuery.
|
|
67
36
|
|
|
@@ -3,6 +3,7 @@ import re
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
5
|
import collections
|
|
6
|
+
import platform
|
|
6
7
|
import copy
|
|
7
8
|
from http import HTTPStatus
|
|
8
9
|
|
|
@@ -33,6 +34,10 @@ from .graphql_queries import (
|
|
|
33
34
|
)
|
|
34
35
|
from .exceptions import (
|
|
35
36
|
FailedOperations,
|
|
37
|
+
UnauthorizedError,
|
|
38
|
+
AuthenticationError,
|
|
39
|
+
ServerNotReached,
|
|
40
|
+
ServerError,
|
|
36
41
|
)
|
|
37
42
|
from .utils import (
|
|
38
43
|
logout_from_server,
|
|
@@ -49,22 +54,6 @@ PROJECT_NAME_REGEX = re.compile(
|
|
|
49
54
|
)
|
|
50
55
|
|
|
51
56
|
|
|
52
|
-
class ServerError(Exception):
|
|
53
|
-
pass
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class UnauthorizedError(ServerError):
|
|
57
|
-
pass
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class AuthenticationError(ServerError):
|
|
61
|
-
pass
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
class ServerNotReached(ServerError):
|
|
65
|
-
pass
|
|
66
|
-
|
|
67
|
-
|
|
68
57
|
class RequestType:
|
|
69
58
|
def __init__(self, name):
|
|
70
59
|
self.name = name
|
|
@@ -81,29 +70,6 @@ class RequestTypes:
|
|
|
81
70
|
delete = RequestType("DELETE")
|
|
82
71
|
|
|
83
72
|
|
|
84
|
-
class MissingEntityError(Exception):
|
|
85
|
-
pass
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
class ProjectNotFound(MissingEntityError):
|
|
89
|
-
def __init__(self, project_name, message=None):
|
|
90
|
-
if not message:
|
|
91
|
-
message = "Project \"{}\" was not found".format(project_name)
|
|
92
|
-
self.project_name = project_name
|
|
93
|
-
super(ProjectNotFound, self).__init__(message)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
class FolderNotFound(MissingEntityError):
|
|
97
|
-
def __init__(self, project_name, folder_id, message=None):
|
|
98
|
-
self.project_name = project_name
|
|
99
|
-
self.folder_id = folder_id
|
|
100
|
-
if not message:
|
|
101
|
-
message = (
|
|
102
|
-
"Folder with id \"{}\" was not found in project \"{}\""
|
|
103
|
-
).format(folder_id, project_name)
|
|
104
|
-
super(FolderNotFound, self).__init__(message)
|
|
105
|
-
|
|
106
|
-
|
|
107
73
|
class RestApiResponse(object):
|
|
108
74
|
"""API Response."""
|
|
109
75
|
|
|
@@ -187,10 +153,21 @@ class ServerAPIBase(object):
|
|
|
187
153
|
Login cause that a session is used
|
|
188
154
|
|
|
189
155
|
Args:
|
|
190
|
-
base_url(str): Example: http://localhost:5000
|
|
156
|
+
base_url (str): Example: http://localhost:5000
|
|
157
|
+
token (str): Access token (api key) to server.
|
|
158
|
+
site_id (str): Unique name of site. Should be the same when
|
|
159
|
+
connection is created from the same machine under same user.
|
|
160
|
+
client_version (str): Version of client application (used in
|
|
161
|
+
desktop client application).
|
|
191
162
|
"""
|
|
192
163
|
|
|
193
|
-
def __init__(
|
|
164
|
+
def __init__(
|
|
165
|
+
self,
|
|
166
|
+
base_url,
|
|
167
|
+
token=None,
|
|
168
|
+
site_id=None,
|
|
169
|
+
client_version=None
|
|
170
|
+
):
|
|
194
171
|
if not base_url:
|
|
195
172
|
raise ValueError("Invalid server URL {}".format(str(base_url)))
|
|
196
173
|
|
|
@@ -200,6 +177,8 @@ class ServerAPIBase(object):
|
|
|
200
177
|
self._graphl_url = "{}/graphql".format(base_url)
|
|
201
178
|
self._log = None
|
|
202
179
|
self._access_token = token
|
|
180
|
+
self._site_id = site_id
|
|
181
|
+
self._client_version = client_version
|
|
203
182
|
self._access_token_is_service = None
|
|
204
183
|
self._token_is_valid = None
|
|
205
184
|
self._server_available = None
|
|
@@ -225,6 +204,20 @@ class ServerAPIBase(object):
|
|
|
225
204
|
def access_token(self):
|
|
226
205
|
return self._access_token
|
|
227
206
|
|
|
207
|
+
def get_site_id(self):
|
|
208
|
+
return self._site_id
|
|
209
|
+
|
|
210
|
+
def set_site_id(self, site_id):
|
|
211
|
+
if self._site_id == site_id:
|
|
212
|
+
return
|
|
213
|
+
self._site_id = site_id
|
|
214
|
+
# Recreate session on machine id change
|
|
215
|
+
if self._session is not None:
|
|
216
|
+
self.close_session()
|
|
217
|
+
self.create_session()
|
|
218
|
+
|
|
219
|
+
site_id = property(get_site_id, set_site_id)
|
|
220
|
+
|
|
228
221
|
@property
|
|
229
222
|
def is_server_available(self):
|
|
230
223
|
if self._server_available is None:
|
|
@@ -249,7 +242,11 @@ class ServerAPIBase(object):
|
|
|
249
242
|
|
|
250
243
|
def validate_token(self):
|
|
251
244
|
try:
|
|
252
|
-
|
|
245
|
+
# TODO add other possible validations
|
|
246
|
+
# - existence of 'user' key in info
|
|
247
|
+
# - validate that 'site_id' is in 'sites' in info
|
|
248
|
+
self.get_info()
|
|
249
|
+
self.get_user()
|
|
253
250
|
self._token_is_valid = True
|
|
254
251
|
|
|
255
252
|
except UnauthorizedError:
|
|
@@ -259,7 +256,7 @@ class ServerAPIBase(object):
|
|
|
259
256
|
def set_token(self, token):
|
|
260
257
|
self.reset_token()
|
|
261
258
|
self._access_token = token
|
|
262
|
-
self.
|
|
259
|
+
self.get_user()
|
|
263
260
|
|
|
264
261
|
def reset_token(self):
|
|
265
262
|
self._access_token = None
|
|
@@ -294,6 +291,23 @@ class ServerAPIBase(object):
|
|
|
294
291
|
self._session_functions_mapping = {}
|
|
295
292
|
session.close()
|
|
296
293
|
|
|
294
|
+
def get_info(self):
|
|
295
|
+
"""Get information about current used api key.
|
|
296
|
+
|
|
297
|
+
By default, the 'info' contains only 'uptime' and 'version'. With
|
|
298
|
+
logged user info also contains information about user and machines on
|
|
299
|
+
which was logged in.
|
|
300
|
+
|
|
301
|
+
Todos:
|
|
302
|
+
Use this method for validation of token instead of 'get_user'.
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Dict[str, Any]: Information from server.
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
response = self.get("info")
|
|
309
|
+
return response.data
|
|
310
|
+
|
|
297
311
|
def _get_user_info(self):
|
|
298
312
|
if self._access_token is None:
|
|
299
313
|
return None
|
|
@@ -315,11 +329,23 @@ class ServerAPIBase(object):
|
|
|
315
329
|
self._access_token_is_service = None
|
|
316
330
|
return None
|
|
317
331
|
|
|
318
|
-
def
|
|
319
|
-
|
|
320
|
-
|
|
332
|
+
def get_users(self):
|
|
333
|
+
# TODO how to find out if user have permission?
|
|
334
|
+
users = self.get("users")
|
|
335
|
+
return users.data
|
|
336
|
+
|
|
337
|
+
def get_user(self, username=None):
|
|
338
|
+
output = None
|
|
339
|
+
if username is None:
|
|
340
|
+
output = self._get_user_info()
|
|
341
|
+
else:
|
|
342
|
+
response = self.get("users/{}".format(username))
|
|
343
|
+
if response.status == 200:
|
|
344
|
+
output = response.data
|
|
345
|
+
|
|
346
|
+
if output is None:
|
|
321
347
|
raise UnauthorizedError("User is not authorized.")
|
|
322
|
-
return
|
|
348
|
+
return output
|
|
323
349
|
|
|
324
350
|
@property
|
|
325
351
|
def log(self):
|
|
@@ -330,7 +356,18 @@ class ServerAPIBase(object):
|
|
|
330
356
|
def get_headers(self, content_type=None):
|
|
331
357
|
if content_type is None:
|
|
332
358
|
content_type = "application/json"
|
|
333
|
-
|
|
359
|
+
|
|
360
|
+
headers = {
|
|
361
|
+
"Content-Type": content_type,
|
|
362
|
+
"x-ayon-platform": platform.system().lower(),
|
|
363
|
+
"x-ayon-hostname": platform.node(),
|
|
364
|
+
}
|
|
365
|
+
if self._site_id is not None:
|
|
366
|
+
headers["x-ayon-site-id"] = self._site_id
|
|
367
|
+
|
|
368
|
+
if self._client_version is not None:
|
|
369
|
+
headers["x-ayon-version"] = self._client_version
|
|
370
|
+
|
|
334
371
|
if self._access_token:
|
|
335
372
|
if self._access_token_is_service:
|
|
336
373
|
headers["X-Api-Key"] = self._access_token
|
|
@@ -342,7 +379,7 @@ class ServerAPIBase(object):
|
|
|
342
379
|
def login(self, username, password):
|
|
343
380
|
if self.has_valid_token:
|
|
344
381
|
try:
|
|
345
|
-
user_info = self.
|
|
382
|
+
user_info = self.get_user()
|
|
346
383
|
except UnauthorizedError:
|
|
347
384
|
user_info = {}
|
|
348
385
|
|
|
@@ -507,6 +544,23 @@ class ServerAPIBase(object):
|
|
|
507
544
|
def delete(self, entrypoint, **kwargs):
|
|
508
545
|
return self.raw_delete(entrypoint, params=kwargs)
|
|
509
546
|
|
|
547
|
+
def download_file(self, endpoint, filepath, chunk_size=None):
|
|
548
|
+
if not chunk_size:
|
|
549
|
+
# 1 MB chunk by default
|
|
550
|
+
chunk_size = 1024 * 1024
|
|
551
|
+
dst_directory = os.path.dirname(filepath)
|
|
552
|
+
if not os.path.exists(dst_directory):
|
|
553
|
+
os.makedirs(dst_directory)
|
|
554
|
+
|
|
555
|
+
with open(filepath, "wb") as f_stream:
|
|
556
|
+
with self.raw_get(endpoint, stream=True) as response:
|
|
557
|
+
for chunk in response.iter_content(chunk_size=chunk_size):
|
|
558
|
+
f_stream.write(chunk)
|
|
559
|
+
|
|
560
|
+
def upload_file(self, endpoint, filepath):
|
|
561
|
+
with open(filepath, "rb") as stream:
|
|
562
|
+
self.raw_post(endpoint, data=stream)
|
|
563
|
+
|
|
510
564
|
def trigger_server_restart(self):
|
|
511
565
|
result = self.post("system/restart")
|
|
512
566
|
if result.status_code != 204:
|
|
@@ -709,6 +763,118 @@ class ServerAPIBase(object):
|
|
|
709
763
|
}
|
|
710
764
|
)
|
|
711
765
|
|
|
766
|
+
def get_addons_info(self, details=True):
|
|
767
|
+
"""Get information about addons available on server.
|
|
768
|
+
|
|
769
|
+
Args:
|
|
770
|
+
details (bool): Detailed data with information how to get client
|
|
771
|
+
code.
|
|
772
|
+
"""
|
|
773
|
+
|
|
774
|
+
endpoint = "addons"
|
|
775
|
+
if details:
|
|
776
|
+
endpoint += "?details=1"
|
|
777
|
+
response = self.get(endpoint)
|
|
778
|
+
if response.status != 200:
|
|
779
|
+
raise ServerError(response.text)
|
|
780
|
+
return response.data
|
|
781
|
+
|
|
782
|
+
def get_dependencies_info(self):
|
|
783
|
+
result = self.get("dependencies")
|
|
784
|
+
return result.data
|
|
785
|
+
|
|
786
|
+
def update_dependency_info(
|
|
787
|
+
self,
|
|
788
|
+
name,
|
|
789
|
+
platform_name,
|
|
790
|
+
size,
|
|
791
|
+
checksum,
|
|
792
|
+
checksum_algorithm=None,
|
|
793
|
+
supported_addons=None,
|
|
794
|
+
python_modules=None,
|
|
795
|
+
sources=None
|
|
796
|
+
):
|
|
797
|
+
"""Update or create dependency package infor by it's identifiers.
|
|
798
|
+
|
|
799
|
+
The endpoint can be used to create or update dependency package.
|
|
800
|
+
|
|
801
|
+
Args:
|
|
802
|
+
name (str): Name of dependency package.
|
|
803
|
+
platform_name (Literal["windows", "linux", "darwin"]): Platform for
|
|
804
|
+
which is dependency package targeted.
|
|
805
|
+
size (int): Size of dependency package in bytes.
|
|
806
|
+
checksum (str): Checksum of archive file where dependecies are.
|
|
807
|
+
checksum_algorithm (str): Algorithm used to calculate checksum.
|
|
808
|
+
By default, is used 'md5' (defined by server).
|
|
809
|
+
supported_addons (Dict[str, str]): Name of addons for which was the
|
|
810
|
+
package created ('{"<addon name>": "<addon version>", ...}').
|
|
811
|
+
python_modules (Dict[str, str]): Python modules in dependencies
|
|
812
|
+
package ('{"<module name>": "<module version>", ...}').
|
|
813
|
+
sources (List[Dict[str, Any]]): Information about sources where
|
|
814
|
+
dependency package is available.
|
|
815
|
+
"""
|
|
816
|
+
|
|
817
|
+
kwargs = {
|
|
818
|
+
key: value
|
|
819
|
+
for key, value in (
|
|
820
|
+
("checksumAlgorithm", checksum_algorithm),
|
|
821
|
+
("supportedAddons", supported_addons),
|
|
822
|
+
("pythonModules", python_modules),
|
|
823
|
+
("sources", sources),
|
|
824
|
+
)
|
|
825
|
+
if value
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
response = self.put(
|
|
829
|
+
"dependencies",
|
|
830
|
+
name=name,
|
|
831
|
+
platform=platform_name,
|
|
832
|
+
size=size,
|
|
833
|
+
checksum=checksum,
|
|
834
|
+
**kwargs
|
|
835
|
+
)
|
|
836
|
+
if response.status not in (200, 201):
|
|
837
|
+
raise ServerError("Failed to create/update dependency")
|
|
838
|
+
return response.data
|
|
839
|
+
|
|
840
|
+
def download_dependency_package(
|
|
841
|
+
self,
|
|
842
|
+
dst_directory,
|
|
843
|
+
package_name,
|
|
844
|
+
platform_name=None
|
|
845
|
+
):
|
|
846
|
+
if platform_name is None:
|
|
847
|
+
platform_name = platform.system().lower()
|
|
848
|
+
|
|
849
|
+
package_filepath = os.path.join(dst_directory, package_name + ".zip")
|
|
850
|
+
self.download_file(
|
|
851
|
+
"dependencies/{}/{}".format(package_name, platform_name),
|
|
852
|
+
package_filepath
|
|
853
|
+
)
|
|
854
|
+
return package_filepath
|
|
855
|
+
|
|
856
|
+
def upload_dependency_package(
|
|
857
|
+
self, filepath, package_name, platform_name=None
|
|
858
|
+
):
|
|
859
|
+
if platform_name is None:
|
|
860
|
+
platform_name = platform.system().lower()
|
|
861
|
+
|
|
862
|
+
self.upload_file(
|
|
863
|
+
"dependencies/{}/{}".format(package_name, platform_name),
|
|
864
|
+
filepath
|
|
865
|
+
)
|
|
866
|
+
|
|
867
|
+
def delete_dependency_package(self, package_name, platform_name=None):
|
|
868
|
+
if platform_name is None:
|
|
869
|
+
platform_name = platform.system().lower()
|
|
870
|
+
|
|
871
|
+
response = self.delete(
|
|
872
|
+
"dependencies/{}/{}".format(package_name, platform_name),
|
|
873
|
+
)
|
|
874
|
+
if response.status != 200:
|
|
875
|
+
raise ServerError("Failed to delete dependency file")
|
|
876
|
+
return response.data
|
|
877
|
+
|
|
712
878
|
# Anatomy presets
|
|
713
879
|
def get_project_anatomy_presets(self, add_default=True):
|
|
714
880
|
result = self.get("anatomy/presets")
|
|
@@ -749,13 +915,32 @@ class ServerAPIBase(object):
|
|
|
749
915
|
return full_settings
|
|
750
916
|
return full_settings["settings"]
|
|
751
917
|
|
|
752
|
-
def
|
|
918
|
+
def get_addon_studio_settings(self, addon_name, addon_version):
|
|
919
|
+
result = self.get(
|
|
920
|
+
"addons/{}/{}/settings".format(addon_name, addon_version)
|
|
921
|
+
)
|
|
922
|
+
if result.status != 200:
|
|
923
|
+
raise ServerError(result.text)
|
|
924
|
+
return result.data
|
|
925
|
+
|
|
926
|
+
def get_addon_project_settings(
|
|
927
|
+
self, addon_name, addon_version, project_name
|
|
928
|
+
):
|
|
753
929
|
result = self.get(
|
|
754
930
|
"addons/{}/{}/settings/{}".format(
|
|
755
|
-
addon_name, addon_version, project_name
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
931
|
+
addon_name, addon_version, project_name
|
|
932
|
+
)
|
|
933
|
+
)
|
|
934
|
+
if result.status != 200:
|
|
935
|
+
raise ServerError(result.text)
|
|
936
|
+
return result.data
|
|
937
|
+
|
|
938
|
+
def get_addon_settings(self, addon_name, addon_version, project_name=None):
|
|
939
|
+
if project_name is None:
|
|
940
|
+
return self.get_addon_studio_settings(addon_name, addon_version)
|
|
941
|
+
return self.get_addon_project_settings(
|
|
942
|
+
addon_name, addon_version, project_name
|
|
943
|
+
)
|
|
759
944
|
|
|
760
945
|
# Entity getters
|
|
761
946
|
def get_rest_project(self, project_name):
|
|
@@ -1900,6 +2085,7 @@ class ServerAPIBase(object):
|
|
|
1900
2085
|
version_ids=[version_id],
|
|
1901
2086
|
active=None,
|
|
1902
2087
|
fields=fields,
|
|
2088
|
+
own_attributes=own_attributes
|
|
1903
2089
|
)
|
|
1904
2090
|
for representation in representations:
|
|
1905
2091
|
return representation
|
|
@@ -1955,7 +2141,7 @@ class ServerAPIBase(object):
|
|
|
1955
2141
|
fields=None,
|
|
1956
2142
|
own_attributes=False
|
|
1957
2143
|
):
|
|
1958
|
-
filters = {}
|
|
2144
|
+
filters = {"projectName": project_name}
|
|
1959
2145
|
if task_ids is not None:
|
|
1960
2146
|
task_ids = set(task_ids)
|
|
1961
2147
|
if not task_ids:
|
|
@@ -1980,7 +2166,7 @@ class ServerAPIBase(object):
|
|
|
1980
2166
|
if own_attributes:
|
|
1981
2167
|
fields.add("ownAttrib")
|
|
1982
2168
|
|
|
1983
|
-
query = workfiles_info_graphql_query()
|
|
2169
|
+
query = workfiles_info_graphql_query(fields)
|
|
1984
2170
|
|
|
1985
2171
|
for attr, filter_value in filters.items():
|
|
1986
2172
|
query.set_variable_value(attr, filter_value)
|
|
@@ -85,6 +85,31 @@ def get_server_api_connection():
|
|
|
85
85
|
return GlobalContext.get_server_api_connection()
|
|
86
86
|
|
|
87
87
|
|
|
88
|
+
def raw_get(*args, **kwargs):
|
|
89
|
+
con = get_server_api_connection()
|
|
90
|
+
return con.raw_get(*args, **kwargs)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def raw_post(*args, **kwargs):
|
|
94
|
+
con = get_server_api_connection()
|
|
95
|
+
return con.raw_post(*args, **kwargs)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def raw_put(*args, **kwargs):
|
|
99
|
+
con = get_server_api_connection()
|
|
100
|
+
return con.raw_put(*args, **kwargs)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def raw_patch(*args, **kwargs):
|
|
104
|
+
con = get_server_api_connection()
|
|
105
|
+
return con.raw_patch(*args, **kwargs)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def raw_delete(*args, **kwargs):
|
|
109
|
+
con = get_server_api_connection()
|
|
110
|
+
return con.raw_delete(*args, **kwargs)
|
|
111
|
+
|
|
112
|
+
|
|
88
113
|
def get(*args, **kwargs):
|
|
89
114
|
con = get_server_api_connection()
|
|
90
115
|
return con.get(*args, **kwargs)
|
|
@@ -110,6 +135,101 @@ def delete(*args, **kwargs):
|
|
|
110
135
|
return con.delete(*args, **kwargs)
|
|
111
136
|
|
|
112
137
|
|
|
138
|
+
def download_file(*args, **kwargs):
|
|
139
|
+
con = get_server_api_connection()
|
|
140
|
+
return con.download_file(*args, **kwargs)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def upload_file(*args, **kwargs):
|
|
144
|
+
con = get_server_api_connection()
|
|
145
|
+
return con.upload_file(*args, **kwargs)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def query_graphql(*args, **kwargs):
|
|
149
|
+
con = get_server_api_connection()
|
|
150
|
+
return con.query_graphql(*args, **kwargs)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def get_users(*args, **kwargs):
|
|
154
|
+
con = get_server_api_connection()
|
|
155
|
+
return con.get_users(*args, **kwargs)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def get_user(*args, **kwargs):
|
|
159
|
+
con = get_server_api_connection()
|
|
160
|
+
return con.get_user(*args, **kwargs)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def get_attributes_for_type(*args, **kwargs):
|
|
164
|
+
con = get_server_api_connection()
|
|
165
|
+
return con.get_attributes_for_type(*args, **kwargs)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def get_addons_info(*args, **kwargs):
|
|
169
|
+
con = get_server_api_connection()
|
|
170
|
+
return con.get_addons_info(*args, **kwargs)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def get_dependencies_info(*args, **kwargs):
|
|
174
|
+
con = get_server_api_connection()
|
|
175
|
+
return con.get_dependencies_info(*args, **kwargs)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def update_dependency_info(*args, **kwargs):
|
|
179
|
+
con = get_server_api_connection()
|
|
180
|
+
return con.update_dependency_info(*args, **kwargs)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def download_dependency_package(*args, **kwargs):
|
|
184
|
+
con = get_server_api_connection()
|
|
185
|
+
return con.download_dependency_package(*args, **kwargs)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def upload_dependency_package(*args, **kwargs):
|
|
189
|
+
con = get_server_api_connection()
|
|
190
|
+
return con.upload_dependency_package(*args, **kwargs)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def delete_dependency_package(*args, **kwargs):
|
|
194
|
+
con = get_server_api_connection()
|
|
195
|
+
return con.delete_dependency_package(*args, **kwargs)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def get_project_anatomy_presets(*args, **kwargs):
|
|
199
|
+
con = get_server_api_connection()
|
|
200
|
+
return con.get_project_anatomy_presets(*args, **kwargs)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def get_project_anatomy_preset(*args, **kwargs):
|
|
204
|
+
con = get_server_api_connection()
|
|
205
|
+
return con.get_project_anatomy_preset(*args, **kwargs)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def get_full_production_settings(*args, **kwargs):
|
|
209
|
+
con = get_server_api_connection()
|
|
210
|
+
return con.get_full_production_settings(*args, **kwargs)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def get_production_settings(*args, **kwargs):
|
|
214
|
+
con = get_server_api_connection()
|
|
215
|
+
return con.get_production_settings(*args, **kwargs)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def get_full_project_settings(*args, **kwargs):
|
|
219
|
+
con = get_server_api_connection()
|
|
220
|
+
return con.get_full_project_settings(*args, **kwargs)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def get_project_settings(*args, **kwargs):
|
|
224
|
+
con = get_server_api_connection()
|
|
225
|
+
return con.get_project_settings(*args, **kwargs)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def get_addon_settings(*args, **kwargs):
|
|
229
|
+
con = get_server_api_connection()
|
|
230
|
+
return con.get_addon_settings(*args, **kwargs)
|
|
231
|
+
|
|
232
|
+
|
|
113
233
|
def get_project(*args, **kwargs):
|
|
114
234
|
con = get_server_api_connection()
|
|
115
235
|
return con.get_project(*args, **kwargs)
|
|
@@ -245,16 +365,6 @@ def get_representations_parents(*args, **kwargs):
|
|
|
245
365
|
return con.get_representations_parents(*args, **kwargs)
|
|
246
366
|
|
|
247
367
|
|
|
248
|
-
def get_project_settings(*args, **kwargs):
|
|
249
|
-
con = get_server_api_connection()
|
|
250
|
-
return con.get_project_settings(*args, **kwargs)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
def get_addon_settings(*args, **kwargs):
|
|
254
|
-
con = get_server_api_connection()
|
|
255
|
-
return con.get_addon_settings(*args, **kwargs)
|
|
256
|
-
|
|
257
|
-
|
|
258
368
|
def create_project(
|
|
259
369
|
project_name,
|
|
260
370
|
project_code,
|
|
@@ -7,6 +7,8 @@ except ImportError:
|
|
|
7
7
|
|
|
8
8
|
import requests
|
|
9
9
|
|
|
10
|
+
from .exceptions import UrlError
|
|
11
|
+
|
|
10
12
|
REMOVED_VALUE = object()
|
|
11
13
|
|
|
12
14
|
|
|
@@ -97,22 +99,6 @@ def prepare_entity_changes(old_entity, new_entity, replace=False):
|
|
|
97
99
|
return changes
|
|
98
100
|
|
|
99
101
|
|
|
100
|
-
class UrlError(Exception):
|
|
101
|
-
"""Url cannot be parsed as url.
|
|
102
|
-
|
|
103
|
-
Exception may contain hints of possible fixes of url that can be used in
|
|
104
|
-
UI if needed.
|
|
105
|
-
"""
|
|
106
|
-
|
|
107
|
-
def __init__(self, message, title, hints=None):
|
|
108
|
-
if hints is None:
|
|
109
|
-
hints = []
|
|
110
|
-
|
|
111
|
-
self.title = title
|
|
112
|
-
self.hints = hints
|
|
113
|
-
super(UrlError, self).__init__(message)
|
|
114
|
-
|
|
115
|
-
|
|
116
102
|
def _try_parse_url(url):
|
|
117
103
|
try:
|
|
118
104
|
return urlparse(url)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""Package declaring Python API for Ayon server."""
|
|
2
|
-
__version__ = "0.1.
|
|
2
|
+
__version__ = "0.1.6"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ayon-python-api-0.1.3 → ayon-python-api-0.1.6}/ayon_python_api.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|