ayon-python-api 1.2.16.dev0__py3-none-any.whl → 1.2.17.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ayon_api/__init__.py +48 -0
- ayon_api/_api.py +426 -2
- ayon_api/_api_helpers/activities.py +112 -1
- ayon_api/_api_helpers/attributes.py +12 -1
- ayon_api/_api_helpers/lists.py +4 -1
- ayon_api/_api_helpers/projects.py +120 -9
- ayon_api/exceptions.py +14 -2
- ayon_api/graphql_queries.py +2 -0
- ayon_api/server_api.py +171 -1
- ayon_api/typing.py +2 -1
- ayon_api/utils.py +102 -98
- ayon_api/version.py +1 -1
- {ayon_python_api-1.2.16.dev0.dist-info → ayon_python_api-1.2.17.dev0.dist-info}/METADATA +1 -1
- {ayon_python_api-1.2.16.dev0.dist-info → ayon_python_api-1.2.17.dev0.dist-info}/RECORD +17 -17
- {ayon_python_api-1.2.16.dev0.dist-info → ayon_python_api-1.2.17.dev0.dist-info}/WHEEL +0 -0
- {ayon_python_api-1.2.16.dev0.dist-info → ayon_python_api-1.2.17.dev0.dist-info}/licenses/LICENSE +0 -0
- {ayon_python_api-1.2.16.dev0.dist-info → ayon_python_api-1.2.17.dev0.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import typing
|
|
5
|
-
from typing import Optional, Iterable, Generator, Any
|
|
5
|
+
from typing import Optional, Iterable, Generator, Any, Literal
|
|
6
6
|
|
|
7
7
|
from ayon_api.utils import (
|
|
8
8
|
SortOrder,
|
|
@@ -255,6 +255,117 @@ class ActivitiesAPI(BaseServerAPI):
|
|
|
255
255
|
)
|
|
256
256
|
response.raise_for_status()
|
|
257
257
|
|
|
258
|
+
def get_raw_activity_categories(self, project_name: str) -> dict[str, Any]:
|
|
259
|
+
"""Get activity categories available on server (raw response).
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
project_name (str): Project name to get categories for.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
list[str]: Available activity categories.
|
|
266
|
+
|
|
267
|
+
"""
|
|
268
|
+
response = self.get(f"projects/{project_name}/activityCategories")
|
|
269
|
+
response.raise_for_status()
|
|
270
|
+
return response.data
|
|
271
|
+
|
|
272
|
+
def get_activity_categories(self, project_name: str) -> list[str]:
|
|
273
|
+
"""Get activity categories available on server.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
project_name (str): Project name to get categories for.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
list[str]: Available activity categories.
|
|
280
|
+
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
data = self.get_raw_activity_categories(project_name)
|
|
284
|
+
return data["categories"]
|
|
285
|
+
|
|
286
|
+
def create_activity_reaction(
|
|
287
|
+
self,
|
|
288
|
+
project_name: str,
|
|
289
|
+
activity_id: str,
|
|
290
|
+
reaction: str,
|
|
291
|
+
) -> None:
|
|
292
|
+
"""React to activity."""
|
|
293
|
+
response = self.post(
|
|
294
|
+
f"projects/{project_name}/activities/{activity_id}/reactions",
|
|
295
|
+
reaction=reaction,
|
|
296
|
+
)
|
|
297
|
+
response.raise_for_status()
|
|
298
|
+
|
|
299
|
+
def delete_activity_reaction(
|
|
300
|
+
self, project_name: str, activity_id: str, reaction: str
|
|
301
|
+
) -> None:
|
|
302
|
+
response = self.delete(
|
|
303
|
+
f"projects/{project_name}/activities/{activity_id}"
|
|
304
|
+
f"/reactions/{reaction}"
|
|
305
|
+
)
|
|
306
|
+
response.raise_for_status()
|
|
307
|
+
|
|
308
|
+
def suggest_entity_mention(
|
|
309
|
+
self,
|
|
310
|
+
project_name: str,
|
|
311
|
+
entity_id: str,
|
|
312
|
+
entity_type: Literal["folder", "task", "version"],
|
|
313
|
+
) -> dict[str, dict[str, Any]]:
|
|
314
|
+
"""Suggest entities for mention in activity body.
|
|
315
|
+
|
|
316
|
+
At this moment does not change data only returns suggestions.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
project_name (str): Project name to search in.
|
|
320
|
+
entity_id (str): Entity id.
|
|
321
|
+
entity_type (str): Entity type of the entity.
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
list[dict[str, Any]]: List of suggested entities with
|
|
325
|
+
their details.
|
|
326
|
+
|
|
327
|
+
"""
|
|
328
|
+
response = self.post(
|
|
329
|
+
f"projects/{project_name}/suggest",
|
|
330
|
+
entity_id=entity_id,
|
|
331
|
+
entity_type=entity_type,
|
|
332
|
+
)
|
|
333
|
+
response.raise_for_status()
|
|
334
|
+
return response.data
|
|
335
|
+
|
|
336
|
+
def get_raw_entity_watchers(
|
|
337
|
+
self, project_name: str, entity_id: str, entity_type: str
|
|
338
|
+
) -> dict[str, Any]:
|
|
339
|
+
"""Get entity watchers (raw response)."""
|
|
340
|
+
response = self.get(
|
|
341
|
+
f"projects/{project_name}/{entity_type}/{entity_id}/watchers"
|
|
342
|
+
)
|
|
343
|
+
response.raise_for_status()
|
|
344
|
+
return response.data
|
|
345
|
+
|
|
346
|
+
def get_entity_watchers(
|
|
347
|
+
self, project_name: str, entity_id: str, entity_type: str
|
|
348
|
+
) -> list[str]:
|
|
349
|
+
"""List watchers of an entity."""
|
|
350
|
+
data = self.get_raw_entity_watchers(
|
|
351
|
+
project_name, entity_id, entity_type
|
|
352
|
+
)
|
|
353
|
+
return data["watchers"]
|
|
354
|
+
|
|
355
|
+
def set_entity_watchers(
|
|
356
|
+
self,
|
|
357
|
+
project_name: str,
|
|
358
|
+
entity_id: str,
|
|
359
|
+
entity_type: str,
|
|
360
|
+
watchers: list[str],
|
|
361
|
+
):
|
|
362
|
+
"""Change watchers of an entity."""
|
|
363
|
+
response = self.post(
|
|
364
|
+
f"projects/{project_name}/{entity_type}/{entity_id}/watchers",
|
|
365
|
+
watchers=watchers,
|
|
366
|
+
)
|
|
367
|
+
response.raise_for_status()
|
|
368
|
+
|
|
258
369
|
def send_activities_batch_operations(
|
|
259
370
|
self,
|
|
260
371
|
project_name: str,
|
|
@@ -139,7 +139,7 @@ class AttributesAPI(BaseServerAPI):
|
|
|
139
139
|
|
|
140
140
|
self.reset_attributes_schema()
|
|
141
141
|
|
|
142
|
-
def
|
|
142
|
+
def delete_attribute_config(self, attribute_name: str) -> None:
|
|
143
143
|
"""Remove attribute from server.
|
|
144
144
|
|
|
145
145
|
This can't be un-done, please use carefully.
|
|
@@ -156,6 +156,17 @@ class AttributesAPI(BaseServerAPI):
|
|
|
156
156
|
|
|
157
157
|
self.reset_attributes_schema()
|
|
158
158
|
|
|
159
|
+
def remove_attribute_config(self, attribute_name: str) -> None:
|
|
160
|
+
"""Remove attribute from server.
|
|
161
|
+
|
|
162
|
+
DEPRECATED: Use 'delete_attribute_config' instead.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
attribute_name (str): Name of attribute to remove.
|
|
166
|
+
|
|
167
|
+
"""
|
|
168
|
+
return self.delete_attribute_config(attribute_name)
|
|
169
|
+
|
|
159
170
|
def get_attributes_for_type(
|
|
160
171
|
self, entity_type: AttributeScope
|
|
161
172
|
) -> dict[str, AttributeSchemaDataDict]:
|
ayon_api/_api_helpers/lists.py
CHANGED
|
@@ -316,6 +316,7 @@ class ListsAPI(BaseServerAPI):
|
|
|
316
316
|
self,
|
|
317
317
|
project_name: str,
|
|
318
318
|
list_id: str,
|
|
319
|
+
entity_id: str,
|
|
319
320
|
*,
|
|
320
321
|
position: Optional[int] = None,
|
|
321
322
|
label: Optional[str] = None,
|
|
@@ -329,6 +330,7 @@ class ListsAPI(BaseServerAPI):
|
|
|
329
330
|
Args:
|
|
330
331
|
project_name (str): Project name where entity list lives.
|
|
331
332
|
list_id (str): Entity list id where item will be added.
|
|
333
|
+
entity_id (str): Id of entity added to the list.
|
|
332
334
|
position (Optional[int]): Position of item in entity list.
|
|
333
335
|
label (Optional[str]): Label of item in entity list.
|
|
334
336
|
attrib (Optional[dict[str, Any]]): Item attribute values.
|
|
@@ -342,9 +344,10 @@ class ListsAPI(BaseServerAPI):
|
|
|
342
344
|
"""
|
|
343
345
|
if item_id is None:
|
|
344
346
|
item_id = create_entity_id()
|
|
347
|
+
|
|
345
348
|
kwargs = {
|
|
346
349
|
"id": item_id,
|
|
347
|
-
"entityId":
|
|
350
|
+
"entityId": entity_id,
|
|
348
351
|
}
|
|
349
352
|
for key, value in (
|
|
350
353
|
("position", position),
|
|
@@ -174,6 +174,7 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
174
174
|
self,
|
|
175
175
|
active: Optional[bool] = True,
|
|
176
176
|
library: Optional[bool] = None,
|
|
177
|
+
include_skeleton: bool = False,
|
|
177
178
|
) -> Generator[ProjectDict, None, None]:
|
|
178
179
|
"""Query available project entities.
|
|
179
180
|
|
|
@@ -184,12 +185,17 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
184
185
|
are returned if 'None' is passed.
|
|
185
186
|
library (Optional[bool]): Filter standard/library projects. Both
|
|
186
187
|
are returned if 'None' is passed.
|
|
188
|
+
include_skeleton (bool): Include skeleton projects.
|
|
187
189
|
|
|
188
190
|
Returns:
|
|
189
191
|
Generator[ProjectDict, None, None]: Available projects.
|
|
190
192
|
|
|
191
193
|
"""
|
|
192
|
-
for project_name in self.get_project_names(
|
|
194
|
+
for project_name in self.get_project_names(
|
|
195
|
+
active=active,
|
|
196
|
+
library=library,
|
|
197
|
+
include_skeleton=include_skeleton,
|
|
198
|
+
):
|
|
193
199
|
project = self.get_rest_project(project_name)
|
|
194
200
|
if project:
|
|
195
201
|
yield project
|
|
@@ -198,6 +204,7 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
198
204
|
self,
|
|
199
205
|
active: Optional[bool] = True,
|
|
200
206
|
library: Optional[bool] = None,
|
|
207
|
+
include_skeleton: bool = False,
|
|
201
208
|
) -> list[ProjectListDict]:
|
|
202
209
|
"""Receive available projects.
|
|
203
210
|
|
|
@@ -208,6 +215,7 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
208
215
|
are returned if 'None' is passed.
|
|
209
216
|
library (Optional[bool]): Filter standard/library projects. Both
|
|
210
217
|
are returned if 'None' is passed.
|
|
218
|
+
include_skeleton (bool): Include skeleton projects.
|
|
211
219
|
|
|
212
220
|
Returns:
|
|
213
221
|
list[ProjectListDict]: List of available projects.
|
|
@@ -219,10 +227,14 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
219
227
|
if library is not None:
|
|
220
228
|
library = "true" if library else "false"
|
|
221
229
|
|
|
222
|
-
|
|
230
|
+
query_data = {
|
|
223
231
|
"active": active,
|
|
224
232
|
"library": library,
|
|
225
|
-
}
|
|
233
|
+
}
|
|
234
|
+
if include_skeleton:
|
|
235
|
+
query_data["skeleton"] = "true"
|
|
236
|
+
|
|
237
|
+
query = prepare_query_string(query_data)
|
|
226
238
|
response = self.get(f"projects{query}")
|
|
227
239
|
response.raise_for_status()
|
|
228
240
|
data = response.data
|
|
@@ -232,6 +244,7 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
232
244
|
self,
|
|
233
245
|
active: Optional[bool] = True,
|
|
234
246
|
library: Optional[bool] = None,
|
|
247
|
+
include_skeleton: bool = False,
|
|
235
248
|
) -> list[str]:
|
|
236
249
|
"""Receive available project names.
|
|
237
250
|
|
|
@@ -242,6 +255,7 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
242
255
|
are returned if 'None' is passed.
|
|
243
256
|
library (Optional[bool]): Filter standard/library projects. Both
|
|
244
257
|
are returned if 'None' is passed.
|
|
258
|
+
include_skeleton (bool): Include skeleton projects.
|
|
245
259
|
|
|
246
260
|
Returns:
|
|
247
261
|
list[str]: List of available project names.
|
|
@@ -249,13 +263,18 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
249
263
|
"""
|
|
250
264
|
return [
|
|
251
265
|
project["name"]
|
|
252
|
-
for project in self.get_rest_projects_list(
|
|
266
|
+
for project in self.get_rest_projects_list(
|
|
267
|
+
active=active,
|
|
268
|
+
library=library,
|
|
269
|
+
include_skeleton=include_skeleton,
|
|
270
|
+
)
|
|
253
271
|
]
|
|
254
272
|
|
|
255
273
|
def get_projects(
|
|
256
274
|
self,
|
|
257
275
|
active: Optional[bool] = True,
|
|
258
276
|
library: Optional[bool] = None,
|
|
277
|
+
include_skeleton: bool = False,
|
|
259
278
|
fields: Optional[Iterable[str]] = None,
|
|
260
279
|
own_attributes: bool = False,
|
|
261
280
|
) -> Generator[ProjectDict, None, None]:
|
|
@@ -266,6 +285,7 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
266
285
|
Filter is disabled when 'None' is passed.
|
|
267
286
|
library (Optional[bool]): Filter library projects. Filter is
|
|
268
287
|
disabled when 'None' is passed.
|
|
288
|
+
include_skeleton (bool): Include skeleton projects.
|
|
269
289
|
fields (Optional[Iterable[str]]): fields to be queried
|
|
270
290
|
for project.
|
|
271
291
|
own_attributes (Optional[bool]): Attribute values that are
|
|
@@ -280,7 +300,11 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
280
300
|
|
|
281
301
|
graphql_fields, fetch_type = self._get_project_graphql_fields(fields)
|
|
282
302
|
if fetch_type == ProjectFetchType.RESTList:
|
|
283
|
-
yield from self.get_rest_projects_list(
|
|
303
|
+
yield from self.get_rest_projects_list(
|
|
304
|
+
active=active,
|
|
305
|
+
library=library,
|
|
306
|
+
include_skeleton=include_skeleton,
|
|
307
|
+
)
|
|
284
308
|
return
|
|
285
309
|
|
|
286
310
|
projects_by_name = {}
|
|
@@ -288,6 +312,7 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
288
312
|
projects = list(self._get_graphql_projects(
|
|
289
313
|
active,
|
|
290
314
|
library,
|
|
315
|
+
include_skeleton=include_skeleton,
|
|
291
316
|
fields=graphql_fields,
|
|
292
317
|
own_attributes=own_attributes,
|
|
293
318
|
))
|
|
@@ -296,7 +321,11 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
296
321
|
return
|
|
297
322
|
projects_by_name = {p["name"]: p for p in projects}
|
|
298
323
|
|
|
299
|
-
for project in self.get_rest_projects(
|
|
324
|
+
for project in self.get_rest_projects(
|
|
325
|
+
active=active,
|
|
326
|
+
library=library,
|
|
327
|
+
include_skeleton=include_skeleton,
|
|
328
|
+
):
|
|
300
329
|
if own_attributes:
|
|
301
330
|
fill_own_attribs(project)
|
|
302
331
|
|
|
@@ -356,6 +385,8 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
356
385
|
project_code: str,
|
|
357
386
|
library_project: bool = False,
|
|
358
387
|
preset_name: Optional[str] = None,
|
|
388
|
+
data: dict[str, Any] | None = None,
|
|
389
|
+
skeleton: bool = False,
|
|
359
390
|
) -> ProjectDict:
|
|
360
391
|
"""Create project using AYON settings.
|
|
361
392
|
|
|
@@ -375,6 +406,8 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
375
406
|
library_project (Optional[bool]): Project is library project.
|
|
376
407
|
preset_name (Optional[str]): Name of anatomy preset. Default is
|
|
377
408
|
used if not passed.
|
|
409
|
+
data (dict[str, Any]): Project data.
|
|
410
|
+
skeleton (bool): Project is skeleton project.
|
|
378
411
|
|
|
379
412
|
Raises:
|
|
380
413
|
ValueError: When project name already exists.
|
|
@@ -395,12 +428,19 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
395
428
|
|
|
396
429
|
preset = self.get_project_anatomy_preset(preset_name)
|
|
397
430
|
|
|
431
|
+
if data is None:
|
|
432
|
+
data = {}
|
|
433
|
+
|
|
434
|
+
if skeleton:
|
|
435
|
+
data["skeleton"] = True
|
|
436
|
+
|
|
398
437
|
result = self.post(
|
|
399
438
|
"projects",
|
|
400
439
|
name=project_name,
|
|
401
440
|
code=project_code,
|
|
402
441
|
anatomy=preset,
|
|
403
|
-
library=library_project
|
|
442
|
+
library=library_project,
|
|
443
|
+
data=data,
|
|
404
444
|
)
|
|
405
445
|
|
|
406
446
|
if result.status != 201:
|
|
@@ -498,6 +538,73 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
498
538
|
f"Failed to delete project \"{project_name}\". {detail}"
|
|
499
539
|
)
|
|
500
540
|
|
|
541
|
+
def get_raw_project_folders(self) -> dict[str, Any]:
|
|
542
|
+
"""Get project folders (raw data)."""
|
|
543
|
+
response = self.get("projectFolders")
|
|
544
|
+
response.raise_for_status()
|
|
545
|
+
return response.data
|
|
546
|
+
|
|
547
|
+
def get_project_folders(self) -> list[dict[str, Any]]:
|
|
548
|
+
data = self.get_raw_project_folders()
|
|
549
|
+
return data["folders"]
|
|
550
|
+
|
|
551
|
+
def create_project_folder(
|
|
552
|
+
self,
|
|
553
|
+
label: str,
|
|
554
|
+
parent_id: str | None = None,
|
|
555
|
+
data: dict[str, Any] | None = None,
|
|
556
|
+
) -> str:
|
|
557
|
+
"""Create project folder."""
|
|
558
|
+
kwargs = {}
|
|
559
|
+
if parent_id is not None:
|
|
560
|
+
kwargs["parentId"] = parent_id
|
|
561
|
+
if data:
|
|
562
|
+
kwargs["data"] = data
|
|
563
|
+
|
|
564
|
+
response = self.post("projectFolders", label=label, **kwargs)
|
|
565
|
+
response.raise_for_status()
|
|
566
|
+
return response.data["id"]
|
|
567
|
+
|
|
568
|
+
def update_project_folder(
|
|
569
|
+
self,
|
|
570
|
+
folder_id: str,
|
|
571
|
+
label: str | None = None,
|
|
572
|
+
parent_id: str | None = None,
|
|
573
|
+
data: dict[str, Any] | None = None,
|
|
574
|
+
) -> None:
|
|
575
|
+
body = {
|
|
576
|
+
key: value
|
|
577
|
+
for key, value in (
|
|
578
|
+
("label", label),
|
|
579
|
+
("parentId", parent_id),
|
|
580
|
+
("data", data),
|
|
581
|
+
)
|
|
582
|
+
if value is not None
|
|
583
|
+
}
|
|
584
|
+
response = self.patch(f"projectFolders/{folder_id}", **body)
|
|
585
|
+
response.raise_for_status()
|
|
586
|
+
|
|
587
|
+
def set_project_folders_order(self, folder_ids: list[str]) -> None:
|
|
588
|
+
"""Set project folders order."""
|
|
589
|
+
response = self.post("projectFolders/order", order=folder_ids)
|
|
590
|
+
response.raise_for_status()
|
|
591
|
+
|
|
592
|
+
def assign_projects_to_project_folder(
|
|
593
|
+
self, folder_id: str, project_names: list[str],
|
|
594
|
+
) -> None:
|
|
595
|
+
"""Assign project folder to project."""
|
|
596
|
+
response = self.post(
|
|
597
|
+
"projectFolders/assign",
|
|
598
|
+
folderId=folder_id,
|
|
599
|
+
projectNames=project_names,
|
|
600
|
+
)
|
|
601
|
+
response.raise_for_status()
|
|
602
|
+
|
|
603
|
+
def delete_project_folder(self, folder_id: str):
|
|
604
|
+
"""Delete project folder."""
|
|
605
|
+
response = self.delete(f"projectFolders/{folder_id}")
|
|
606
|
+
response.raise_for_status()
|
|
607
|
+
|
|
501
608
|
def get_project_root_overrides(
|
|
502
609
|
self, project_name: str
|
|
503
610
|
) -> dict[str, dict[str, str]]:
|
|
@@ -792,8 +899,9 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
792
899
|
|
|
793
900
|
def _get_graphql_projects(
|
|
794
901
|
self,
|
|
795
|
-
active:
|
|
796
|
-
library:
|
|
902
|
+
active: bool | None,
|
|
903
|
+
library: bool | None,
|
|
904
|
+
include_skeleton: bool,
|
|
797
905
|
fields: set[str],
|
|
798
906
|
own_attributes: bool,
|
|
799
907
|
project_name: Optional[str] = None
|
|
@@ -810,6 +918,9 @@ class ProjectsAPI(BaseServerAPI):
|
|
|
810
918
|
if project_name is not None:
|
|
811
919
|
query.set_variable_value("projectName", project_name)
|
|
812
920
|
|
|
921
|
+
if include_skeleton:
|
|
922
|
+
query.set_variable_value("skeleton", True)
|
|
923
|
+
|
|
813
924
|
attributes = {}
|
|
814
925
|
if "allAttrib" in fields:
|
|
815
926
|
attributes = self.get_attributes_for_type("project")
|
ayon_api/exceptions.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import copy
|
|
2
4
|
|
|
3
5
|
try:
|
|
@@ -21,13 +23,23 @@ class UrlError(Exception):
|
|
|
21
23
|
UI if needed.
|
|
22
24
|
"""
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
message: str,
|
|
30
|
+
title: str,
|
|
31
|
+
hints: list[str] | None = None,
|
|
32
|
+
) -> None:
|
|
25
33
|
if hints is None:
|
|
26
34
|
hints = []
|
|
27
35
|
|
|
28
36
|
self.title = title
|
|
29
37
|
self.hints = hints
|
|
30
|
-
super(
|
|
38
|
+
super().__init__(message)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class UrlNotReached(UrlError):
|
|
42
|
+
pass
|
|
31
43
|
|
|
32
44
|
|
|
33
45
|
class ServerError(Exception):
|
ayon_api/graphql_queries.py
CHANGED
|
@@ -91,8 +91,10 @@ def project_graphql_query(fields):
|
|
|
91
91
|
def projects_graphql_query(fields):
|
|
92
92
|
query = GraphQlQuery("ProjectsQuery")
|
|
93
93
|
project_name_var = query.add_variable("projectName", "String!")
|
|
94
|
+
skeleton_var = query.add_variable("skeleton", "Boolean!")
|
|
94
95
|
projects_field = query.add_field_with_edges("projects")
|
|
95
96
|
projects_field.set_filter("name", project_name_var)
|
|
97
|
+
projects_field.set_filter("includeSkeleton", skeleton_var)
|
|
96
98
|
|
|
97
99
|
nested_fields = fields_to_dict(fields)
|
|
98
100
|
|
ayon_api/server_api.py
CHANGED
|
@@ -16,7 +16,7 @@ import platform
|
|
|
16
16
|
import uuid
|
|
17
17
|
from contextlib import contextmanager
|
|
18
18
|
import typing
|
|
19
|
-
from typing import Optional, Iterable, Generator, Any, Union
|
|
19
|
+
from typing import Optional, Iterable, Generator, Any, Union, Literal
|
|
20
20
|
|
|
21
21
|
import requests
|
|
22
22
|
|
|
@@ -1361,6 +1361,176 @@ class ServerAPI(
|
|
|
1361
1361
|
def delete(self, entrypoint: str, **kwargs):
|
|
1362
1362
|
return self.raw_delete(entrypoint, params=kwargs)
|
|
1363
1363
|
|
|
1364
|
+
def get_server_config(self):
|
|
1365
|
+
response = self.get("config")
|
|
1366
|
+
response.raise_for_status()
|
|
1367
|
+
return response.data
|
|
1368
|
+
|
|
1369
|
+
def set_server_config(
|
|
1370
|
+
self,
|
|
1371
|
+
studio_name: str | None = None,
|
|
1372
|
+
customization: dict[str, Any] | None = None,
|
|
1373
|
+
authentication: dict[str, Any] | None = None,
|
|
1374
|
+
project_options: dict[str, Any] | None = None,
|
|
1375
|
+
changelog: dict[str, Any] | None = None,
|
|
1376
|
+
) -> None:
|
|
1377
|
+
body = {
|
|
1378
|
+
key: value
|
|
1379
|
+
for key, value in (
|
|
1380
|
+
("studio_name", studio_name),
|
|
1381
|
+
("customization", customization),
|
|
1382
|
+
("authentication", authentication),
|
|
1383
|
+
("project_options", project_options),
|
|
1384
|
+
("changelog", changelog),
|
|
1385
|
+
)
|
|
1386
|
+
if value is not None
|
|
1387
|
+
}
|
|
1388
|
+
response = self.post("config", **body)
|
|
1389
|
+
response.raise_for_status()
|
|
1390
|
+
|
|
1391
|
+
def get_server_config_overrides(self):
|
|
1392
|
+
response = self.get("config/overrides")
|
|
1393
|
+
response.raise_for_status()
|
|
1394
|
+
return response.data
|
|
1395
|
+
|
|
1396
|
+
def get_server_config_value(self, key: str):
|
|
1397
|
+
response = self.get(f"config/value/{key}")
|
|
1398
|
+
response.raise_for_status()
|
|
1399
|
+
return response.data
|
|
1400
|
+
|
|
1401
|
+
def download_server_config_file(
|
|
1402
|
+
self,
|
|
1403
|
+
file_type: Literal["login_background", "studio_logo"],
|
|
1404
|
+
filepath: str,
|
|
1405
|
+
*,
|
|
1406
|
+
chunk_size: Optional[int] = None,
|
|
1407
|
+
progress: Optional[TransferProgress] = None,
|
|
1408
|
+
) -> TransferProgress:
|
|
1409
|
+
"""Download server config file.
|
|
1410
|
+
|
|
1411
|
+
Validate if server has config file available first. Method crashes
|
|
1412
|
+
if the file is not available.
|
|
1413
|
+
|
|
1414
|
+
Args:
|
|
1415
|
+
file_type (Literal["login_background", "studio_logo"]): File to
|
|
1416
|
+
download.
|
|
1417
|
+
filepath (str): Target filepath.
|
|
1418
|
+
chunk_size (int | None): Size of chunks used for download.
|
|
1419
|
+
progress (TransferProgress | None): Object to track download
|
|
1420
|
+
progress.
|
|
1421
|
+
|
|
1422
|
+
"""
|
|
1423
|
+
return self.download_file(
|
|
1424
|
+
f"api/config/files/{file_type}",
|
|
1425
|
+
filepath,
|
|
1426
|
+
chunk_size=chunk_size,
|
|
1427
|
+
progress=progress,
|
|
1428
|
+
)
|
|
1429
|
+
|
|
1430
|
+
def download_server_config_file_to_stream(
|
|
1431
|
+
self,
|
|
1432
|
+
file_type: Literal["login_background", "studio_logo"],
|
|
1433
|
+
stream: StreamType,
|
|
1434
|
+
*,
|
|
1435
|
+
chunk_size: Optional[int] = None,
|
|
1436
|
+
progress: Optional[TransferProgress] = None,
|
|
1437
|
+
) -> TransferProgress:
|
|
1438
|
+
"""Download server config file to byte stream.
|
|
1439
|
+
|
|
1440
|
+
Validate if server has config file available first. Method crashes
|
|
1441
|
+
if the file is not available.
|
|
1442
|
+
|
|
1443
|
+
Args:
|
|
1444
|
+
file_type (Literal["login_background", "studio_logo"]): File to
|
|
1445
|
+
download.
|
|
1446
|
+
stream (StreamType): Stream where downloaded content is stored.
|
|
1447
|
+
chunk_size (int | None): Size of chunks used for download.
|
|
1448
|
+
progress (TransferProgress | None): Object to track download
|
|
1449
|
+
progress.
|
|
1450
|
+
|
|
1451
|
+
"""
|
|
1452
|
+
return self.download_file_to_stream(
|
|
1453
|
+
f"api/config/files/{file_type}",
|
|
1454
|
+
stream,
|
|
1455
|
+
chunk_size=chunk_size,
|
|
1456
|
+
progress=progress,
|
|
1457
|
+
)
|
|
1458
|
+
|
|
1459
|
+
def upload_server_config_file(
|
|
1460
|
+
self,
|
|
1461
|
+
file_type: Literal["login_background", "studio_logo"],
|
|
1462
|
+
filepath: str,
|
|
1463
|
+
*,
|
|
1464
|
+
content_type: str | None = None,
|
|
1465
|
+
filename: str | None = None,
|
|
1466
|
+
chunk_size: int | None = None,
|
|
1467
|
+
progress: TransferProgress | None = None,
|
|
1468
|
+
) -> requests.Response:
|
|
1469
|
+
"""Upload server config file from byte stream.
|
|
1470
|
+
|
|
1471
|
+
TODO create filename using file_type and extension from content_type
|
|
1472
|
+
if filename is not specified
|
|
1473
|
+
|
|
1474
|
+
Args:
|
|
1475
|
+
file_type (Literal["login_background", "studio_logo"]): File to
|
|
1476
|
+
download.
|
|
1477
|
+
filepath (str): Filepath used to store the file.
|
|
1478
|
+
chunk_size (int | None): Size of chunks used for download.
|
|
1479
|
+
progress (TransferProgress | None): Object to track download
|
|
1480
|
+
progress.
|
|
1481
|
+
|
|
1482
|
+
Returns:
|
|
1483
|
+
requests.Response: Response from upload.
|
|
1484
|
+
|
|
1485
|
+
"""
|
|
1486
|
+
if not filename:
|
|
1487
|
+
filename = os.path.basename(filepath)
|
|
1488
|
+
return self.upload_file(
|
|
1489
|
+
f"api/config/files/{file_type}",
|
|
1490
|
+
filepath,
|
|
1491
|
+
filename=filename,
|
|
1492
|
+
content_type=content_type,
|
|
1493
|
+
chunk_size=chunk_size,
|
|
1494
|
+
progress=progress,
|
|
1495
|
+
)
|
|
1496
|
+
|
|
1497
|
+
def upload_server_config_file_from_stream(
|
|
1498
|
+
self,
|
|
1499
|
+
file_type: Literal["login_background", "studio_logo"],
|
|
1500
|
+
stream: StreamType,
|
|
1501
|
+
filename: str,
|
|
1502
|
+
*,
|
|
1503
|
+
content_type: str | None = None,
|
|
1504
|
+
chunk_size: int | None = None,
|
|
1505
|
+
progress: TransferProgress | None = None,
|
|
1506
|
+
) -> requests.Response:
|
|
1507
|
+
"""Upload server config file from byte stream.
|
|
1508
|
+
|
|
1509
|
+
TODO create filename using file_type and extension from content_type
|
|
1510
|
+
if filename is not specified
|
|
1511
|
+
|
|
1512
|
+
Args:
|
|
1513
|
+
file_type (Literal["login_background", "studio_logo"]): File to
|
|
1514
|
+
download.
|
|
1515
|
+
stream (StreamType): Stream where downloaded content is stored.
|
|
1516
|
+
filename (str): Filename used to store the file.
|
|
1517
|
+
chunk_size (int | None): Size of chunks used for download.
|
|
1518
|
+
progress (TransferProgress | None): Object to track download
|
|
1519
|
+
progress.
|
|
1520
|
+
|
|
1521
|
+
Returns:
|
|
1522
|
+
requests.Response: Response from upload.
|
|
1523
|
+
|
|
1524
|
+
"""
|
|
1525
|
+
return self.upload_file_from_stream(
|
|
1526
|
+
f"api/config/files/{file_type}",
|
|
1527
|
+
stream,
|
|
1528
|
+
filename=filename,
|
|
1529
|
+
content_type=content_type,
|
|
1530
|
+
chunk_size=chunk_size,
|
|
1531
|
+
progress=progress,
|
|
1532
|
+
)
|
|
1533
|
+
|
|
1364
1534
|
def _endpoint_to_url(
|
|
1365
1535
|
self,
|
|
1366
1536
|
endpoint: str,
|