mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge pull request #5783 from ynput/feature/OP-6631_Launcher-in-dev-mode
AYON: Support dev bundles
This commit is contained in:
commit
d8dcd91a93
10 changed files with 299 additions and 117 deletions
|
|
@ -31,13 +31,13 @@ from openpype.settings.lib import (
|
|||
get_studio_system_settings_overrides,
|
||||
load_json_file
|
||||
)
|
||||
from openpype.settings.ayon_settings import is_dev_mode_enabled
|
||||
|
||||
from openpype.lib import (
|
||||
Logger,
|
||||
import_filepath,
|
||||
import_module_from_dirpath,
|
||||
)
|
||||
from openpype.lib.openpype_version import is_staging_enabled
|
||||
|
||||
from .interfaces import (
|
||||
OpenPypeInterface,
|
||||
|
|
@ -317,21 +317,10 @@ def load_modules(force=False):
|
|||
time.sleep(0.1)
|
||||
|
||||
|
||||
def _get_ayon_addons_information():
|
||||
"""Receive information about addons to use from server.
|
||||
|
||||
Todos:
|
||||
Actually ask server for the information.
|
||||
Allow project name as optional argument to be able to query information
|
||||
about used addons for specific project.
|
||||
Returns:
|
||||
List[Dict[str, Any]]: List of addon information to use.
|
||||
"""
|
||||
|
||||
output = []
|
||||
def _get_ayon_bundle_data():
|
||||
bundle_name = os.getenv("AYON_BUNDLE_NAME")
|
||||
bundles = ayon_api.get_bundles()["bundles"]
|
||||
final_bundle = next(
|
||||
return next(
|
||||
(
|
||||
bundle
|
||||
for bundle in bundles
|
||||
|
|
@ -339,10 +328,22 @@ def _get_ayon_addons_information():
|
|||
),
|
||||
None
|
||||
)
|
||||
if final_bundle is None:
|
||||
return output
|
||||
|
||||
bundle_addons = final_bundle["addons"]
|
||||
|
||||
def _get_ayon_addons_information(bundle_info):
|
||||
"""Receive information about addons to use from server.
|
||||
|
||||
Todos:
|
||||
Actually ask server for the information.
|
||||
Allow project name as optional argument to be able to query information
|
||||
about used addons for specific project.
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]: List of addon information to use.
|
||||
"""
|
||||
|
||||
output = []
|
||||
bundle_addons = bundle_info["addons"]
|
||||
addons = ayon_api.get_addons_info()["addons"]
|
||||
for addon in addons:
|
||||
name = addon["name"]
|
||||
|
|
@ -378,38 +379,73 @@ def _load_ayon_addons(openpype_modules, modules_key, log):
|
|||
|
||||
v3_addons_to_skip = []
|
||||
|
||||
addons_info = _get_ayon_addons_information()
|
||||
bundle_info = _get_ayon_bundle_data()
|
||||
addons_info = _get_ayon_addons_information(bundle_info)
|
||||
if not addons_info:
|
||||
return v3_addons_to_skip
|
||||
|
||||
addons_dir = os.environ.get("AYON_ADDONS_DIR")
|
||||
if not addons_dir:
|
||||
addons_dir = os.path.join(
|
||||
appdirs.user_data_dir("AYON", "Ynput"),
|
||||
"addons"
|
||||
)
|
||||
if not os.path.exists(addons_dir):
|
||||
|
||||
dev_mode_enabled = is_dev_mode_enabled()
|
||||
dev_addons_info = {}
|
||||
if dev_mode_enabled:
|
||||
# Get dev addons info only when dev mode is enabled
|
||||
dev_addons_info = bundle_info.get("addonDevelopment", dev_addons_info)
|
||||
|
||||
addons_dir_exists = os.path.exists(addons_dir)
|
||||
if not addons_dir_exists:
|
||||
log.warning("Addons directory does not exists. Path \"{}\"".format(
|
||||
addons_dir
|
||||
))
|
||||
return v3_addons_to_skip
|
||||
|
||||
for addon_info in addons_info:
|
||||
addon_name = addon_info["name"]
|
||||
addon_version = addon_info["version"]
|
||||
|
||||
folder_name = "{}_{}".format(addon_name, addon_version)
|
||||
addon_dir = os.path.join(addons_dir, folder_name)
|
||||
if not os.path.exists(addon_dir):
|
||||
log.debug((
|
||||
"No localized client code found for addon {} {}."
|
||||
).format(addon_name, addon_version))
|
||||
dev_addon_info = dev_addons_info.get(addon_name, {})
|
||||
use_dev_path = dev_addon_info.get("enabled", False)
|
||||
|
||||
addon_dir = None
|
||||
if use_dev_path:
|
||||
addon_dir = dev_addon_info["path"]
|
||||
if not addon_dir or not os.path.exists(addon_dir):
|
||||
log.warning((
|
||||
"Dev addon {} {} path does not exists. Path \"{}\""
|
||||
).format(addon_name, addon_version, addon_dir))
|
||||
continue
|
||||
|
||||
elif addons_dir_exists:
|
||||
folder_name = "{}_{}".format(addon_name, addon_version)
|
||||
addon_dir = os.path.join(addons_dir, folder_name)
|
||||
if not os.path.exists(addon_dir):
|
||||
log.debug((
|
||||
"No localized client code found for addon {} {}."
|
||||
).format(addon_name, addon_version))
|
||||
continue
|
||||
|
||||
if not addon_dir:
|
||||
continue
|
||||
|
||||
sys.path.insert(0, addon_dir)
|
||||
imported_modules = []
|
||||
for name in os.listdir(addon_dir):
|
||||
# Ignore of files is implemented to be able to run code from code
|
||||
# where usually is more files than just the addon
|
||||
# Ignore start and setup scripts
|
||||
if name in ("setup.py", "start.py"):
|
||||
continue
|
||||
|
||||
path = os.path.join(addon_dir, name)
|
||||
basename, ext = os.path.splitext(name)
|
||||
# Ignore folders/files with dot in name
|
||||
# - dot names cannot be imported in Python
|
||||
if "." in basename:
|
||||
continue
|
||||
is_dir = os.path.isdir(path)
|
||||
is_py_file = ext.lower() == ".py"
|
||||
if not is_py_file and not is_dir:
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ def get_openpype_staging_icon_filepath():
|
|||
|
||||
|
||||
def get_openpype_icon_filepath(staging=None):
|
||||
if AYON_SERVER_ENABLED and os.getenv("AYON_USE_DEV") == "1":
|
||||
return get_resource("icons", "AYON_icon_dev.png")
|
||||
|
||||
if staging is None:
|
||||
staging = is_running_staging()
|
||||
|
||||
|
|
@ -68,7 +71,9 @@ def get_openpype_splash_filepath(staging=None):
|
|||
staging = is_running_staging()
|
||||
|
||||
if AYON_SERVER_ENABLED:
|
||||
if staging:
|
||||
if os.getenv("AYON_USE_DEV") == "1":
|
||||
splash_file_name = "AYON_splash_dev.png"
|
||||
elif staging:
|
||||
splash_file_name = "AYON_splash_staging.png"
|
||||
else:
|
||||
splash_file_name = "AYON_splash.png"
|
||||
|
|
|
|||
BIN
openpype/resources/icons/AYON_icon_dev.png
Normal file
BIN
openpype/resources/icons/AYON_icon_dev.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
openpype/resources/icons/AYON_splash_dev.png
Normal file
BIN
openpype/resources/icons/AYON_splash_dev.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
|
|
@ -290,6 +290,16 @@ def _convert_modules_system(
|
|||
modules_settings[key] = value
|
||||
|
||||
|
||||
def is_dev_mode_enabled():
|
||||
"""Dev mode is enabled in AYON.
|
||||
|
||||
Returns:
|
||||
bool: True if dev mode is enabled.
|
||||
"""
|
||||
|
||||
return os.getenv("AYON_USE_DEV") == "1"
|
||||
|
||||
|
||||
def convert_system_settings(ayon_settings, default_settings, addon_versions):
|
||||
default_settings = copy.deepcopy(default_settings)
|
||||
output = {
|
||||
|
|
@ -1400,15 +1410,39 @@ class _AyonSettingsCache:
|
|||
if _AyonSettingsCache.variant is None:
|
||||
from openpype.lib.openpype_version import is_staging_enabled
|
||||
|
||||
_AyonSettingsCache.variant = (
|
||||
"staging" if is_staging_enabled() else "production"
|
||||
)
|
||||
variant = "production"
|
||||
if is_dev_mode_enabled():
|
||||
variant = cls._get_dev_mode_settings_variant()
|
||||
elif is_staging_enabled():
|
||||
variant = "staging"
|
||||
_AyonSettingsCache.variant = variant
|
||||
return _AyonSettingsCache.variant
|
||||
|
||||
@classmethod
|
||||
def _get_bundle_name(cls):
|
||||
return os.environ["AYON_BUNDLE_NAME"]
|
||||
|
||||
@classmethod
|
||||
def _get_dev_mode_settings_variant(cls):
|
||||
"""Develop mode settings variant.
|
||||
|
||||
Returns:
|
||||
str: Name of settings variant.
|
||||
"""
|
||||
|
||||
bundles = ayon_api.get_bundles()
|
||||
user = ayon_api.get_user()
|
||||
username = user["name"]
|
||||
for bundle in bundles:
|
||||
if (
|
||||
bundle.get("isDev")
|
||||
and bundle.get("activeUser") == username
|
||||
):
|
||||
return bundle["name"]
|
||||
# Return fake variant - distribution logic will tell user that he
|
||||
# does not have set any dev bundle
|
||||
return "dev"
|
||||
|
||||
@classmethod
|
||||
def get_value_by_project(cls, project_name):
|
||||
cache_item = _AyonSettingsCache.cache_by_project_name[project_name]
|
||||
|
|
|
|||
14
openpype/vendor/python/common/ayon_api/_api.py
vendored
14
openpype/vendor/python/common/ayon_api/_api.py
vendored
|
|
@ -602,12 +602,12 @@ def delete_installer(*args, **kwargs):
|
|||
|
||||
def download_installer(*args, **kwargs):
|
||||
con = get_server_api_connection()
|
||||
con.download_installer(*args, **kwargs)
|
||||
return con.download_installer(*args, **kwargs)
|
||||
|
||||
|
||||
def upload_installer(*args, **kwargs):
|
||||
con = get_server_api_connection()
|
||||
con.upload_installer(*args, **kwargs)
|
||||
return con.upload_installer(*args, **kwargs)
|
||||
|
||||
|
||||
# Dependency packages
|
||||
|
|
@ -753,12 +753,12 @@ def get_secrets(*args, **kwargs):
|
|||
|
||||
def get_secret(*args, **kwargs):
|
||||
con = get_server_api_connection()
|
||||
return con.delete_secret(*args, **kwargs)
|
||||
return con.get_secret(*args, **kwargs)
|
||||
|
||||
|
||||
def save_secret(*args, **kwargs):
|
||||
con = get_server_api_connection()
|
||||
return con.delete_secret(*args, **kwargs)
|
||||
return con.save_secret(*args, **kwargs)
|
||||
|
||||
|
||||
def delete_secret(*args, **kwargs):
|
||||
|
|
@ -978,12 +978,14 @@ def delete_project(project_name):
|
|||
|
||||
def get_thumbnail_by_id(project_name, thumbnail_id):
|
||||
con = get_server_api_connection()
|
||||
con.get_thumbnail_by_id(project_name, thumbnail_id)
|
||||
return con.get_thumbnail_by_id(project_name, thumbnail_id)
|
||||
|
||||
|
||||
def get_thumbnail(project_name, entity_type, entity_id, thumbnail_id=None):
|
||||
con = get_server_api_connection()
|
||||
con.get_thumbnail(project_name, entity_type, entity_id, thumbnail_id)
|
||||
return con.get_thumbnail(
|
||||
project_name, entity_type, entity_id, thumbnail_id
|
||||
)
|
||||
|
||||
|
||||
def get_folder_thumbnail(project_name, folder_id, thumbnail_id=None):
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ def product_types_query(fields):
|
|||
query_queue.append((k, v, field))
|
||||
return query
|
||||
|
||||
|
||||
def project_product_types_query(fields):
|
||||
query = GraphQlQuery("ProjectProductTypes")
|
||||
project_query = query.add_field("project")
|
||||
|
|
@ -175,6 +176,8 @@ def folders_graphql_query(fields):
|
|||
parent_folder_ids_var = query.add_variable("parentFolderIds", "[String!]")
|
||||
folder_paths_var = query.add_variable("folderPaths", "[String!]")
|
||||
folder_names_var = query.add_variable("folderNames", "[String!]")
|
||||
folder_types_var = query.add_variable("folderTypes", "[String!]")
|
||||
statuses_var = query.add_variable("folderStatuses", "[String!]")
|
||||
has_products_var = query.add_variable("folderHasProducts", "Boolean!")
|
||||
|
||||
project_field = query.add_field("project")
|
||||
|
|
@ -185,6 +188,8 @@ def folders_graphql_query(fields):
|
|||
folders_field.set_filter("parentIds", parent_folder_ids_var)
|
||||
folders_field.set_filter("names", folder_names_var)
|
||||
folders_field.set_filter("paths", folder_paths_var)
|
||||
folders_field.set_filter("folderTypes", folder_types_var)
|
||||
folders_field.set_filter("statuses", statuses_var)
|
||||
folders_field.set_filter("hasProducts", has_products_var)
|
||||
|
||||
nested_fields = fields_to_dict(fields)
|
||||
|
|
|
|||
177
openpype/vendor/python/common/ayon_api/server_api.py
vendored
177
openpype/vendor/python/common/ayon_api/server_api.py
vendored
|
|
@ -75,6 +75,7 @@ from .utils import (
|
|||
TransferProgress,
|
||||
create_dependency_package_basename,
|
||||
ThumbnailContent,
|
||||
get_default_timeout,
|
||||
)
|
||||
|
||||
PatternType = type(re.compile(""))
|
||||
|
|
@ -351,7 +352,6 @@ class ServerAPI(object):
|
|||
timeout (Optional[float]): Timeout for requests.
|
||||
max_retries (Optional[int]): Number of retries for requests.
|
||||
"""
|
||||
_default_timeout = 10.0
|
||||
_default_max_retries = 3
|
||||
|
||||
def __init__(
|
||||
|
|
@ -500,20 +500,13 @@ class ServerAPI(object):
|
|||
def get_default_timeout(cls):
|
||||
"""Default value for requests timeout.
|
||||
|
||||
First looks for environment variable SERVER_TIMEOUT_ENV_KEY which
|
||||
can affect timeout value. If not available then use class
|
||||
attribute '_default_timeout'.
|
||||
Utils function 'get_default_timeout' is used by default.
|
||||
|
||||
Returns:
|
||||
float: Timeout value in seconds.
|
||||
"""
|
||||
|
||||
try:
|
||||
return float(os.environ.get(SERVER_TIMEOUT_ENV_KEY))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
return cls._default_timeout
|
||||
return get_default_timeout()
|
||||
|
||||
@classmethod
|
||||
def get_default_max_retries(cls):
|
||||
|
|
@ -662,13 +655,10 @@ class ServerAPI(object):
|
|||
as default variant.
|
||||
|
||||
Args:
|
||||
variant (Literal['production', 'staging']): Settings variant name.
|
||||
variant (str): Settings variant name. It is possible to use
|
||||
'production', 'staging' or name of dev bundle.
|
||||
"""
|
||||
|
||||
if variant not in ("production", "staging"):
|
||||
raise ValueError((
|
||||
"Invalid variant name {}. Expected 'production' or 'staging'"
|
||||
).format(variant))
|
||||
self._default_settings_variant = variant
|
||||
|
||||
default_settings_variant = property(
|
||||
|
|
@ -938,8 +928,8 @@ class ServerAPI(object):
|
|||
int(re_match.group("major")),
|
||||
int(re_match.group("minor")),
|
||||
int(re_match.group("patch")),
|
||||
re_match.group("prerelease"),
|
||||
re_match.group("buildmetadata")
|
||||
re_match.group("prerelease") or "",
|
||||
re_match.group("buildmetadata") or "",
|
||||
)
|
||||
return self._server_version_tuple
|
||||
|
||||
|
|
@ -1140,31 +1130,41 @@ class ServerAPI(object):
|
|||
|
||||
response = None
|
||||
new_response = None
|
||||
for _ in range(max_retries):
|
||||
for retry_idx in reversed(range(max_retries)):
|
||||
try:
|
||||
response = function(url, **kwargs)
|
||||
break
|
||||
|
||||
except ConnectionRefusedError:
|
||||
if retry_idx == 0:
|
||||
self.log.warning(
|
||||
"Connection error happened.", exc_info=True
|
||||
)
|
||||
|
||||
# Server may be restarting
|
||||
new_response = RestApiResponse(
|
||||
None,
|
||||
{"detail": "Unable to connect the server. Connection refused"}
|
||||
)
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
# Connection timed out
|
||||
new_response = RestApiResponse(
|
||||
None,
|
||||
{"detail": "Connection timed out."}
|
||||
)
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
# Other connection error (ssl, etc) - does not make sense to
|
||||
# try call server again
|
||||
# Log warning only on last attempt
|
||||
if retry_idx == 0:
|
||||
self.log.warning(
|
||||
"Connection error happened.", exc_info=True
|
||||
)
|
||||
|
||||
new_response = RestApiResponse(
|
||||
None,
|
||||
{"detail": "Unable to connect the server. Connection error"}
|
||||
)
|
||||
break
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
|
|
@ -1349,7 +1349,9 @@ class ServerAPI(object):
|
|||
status=None,
|
||||
description=None,
|
||||
summary=None,
|
||||
payload=None
|
||||
payload=None,
|
||||
progress=None,
|
||||
retries=None
|
||||
):
|
||||
kwargs = {
|
||||
key: value
|
||||
|
|
@ -1360,9 +1362,27 @@ class ServerAPI(object):
|
|||
("description", description),
|
||||
("summary", summary),
|
||||
("payload", payload),
|
||||
("progress", progress),
|
||||
("retries", retries),
|
||||
)
|
||||
if value is not None
|
||||
}
|
||||
# 'progress' and 'retries' are available since 0.5.x server version
|
||||
major, minor, _, _, _ = self.server_version_tuple
|
||||
if (major, minor) < (0, 5):
|
||||
args = []
|
||||
if progress is not None:
|
||||
args.append("progress")
|
||||
if retries is not None:
|
||||
args.append("retries")
|
||||
fields = ", ".join("'{}'".format(f) for f in args)
|
||||
ending = "s" if len(args) > 1 else ""
|
||||
raise ValueError((
|
||||
"Your server version '{}' does not support update"
|
||||
" of {} field{} on event. The fields are supported since"
|
||||
" server version '0.5'."
|
||||
).format(self.get_server_version(), fields, ending))
|
||||
|
||||
response = self.patch(
|
||||
"events/{}".format(event_id),
|
||||
**kwargs
|
||||
|
|
@ -1434,6 +1454,7 @@ class ServerAPI(object):
|
|||
description=None,
|
||||
sequential=None,
|
||||
events_filter=None,
|
||||
max_retries=None,
|
||||
):
|
||||
"""Enroll job based on events.
|
||||
|
||||
|
|
@ -1475,8 +1496,12 @@ class ServerAPI(object):
|
|||
in target event.
|
||||
sequential (Optional[bool]): The source topic must be processed
|
||||
in sequence.
|
||||
events_filter (Optional[ayon_server.sqlfilter.Filter]): A dict-like
|
||||
with conditions to filter the source event.
|
||||
events_filter (Optional[dict[str, Any]]): Filtering conditions
|
||||
to filter the source event. For more technical specifications
|
||||
look to server backed 'ayon_server.sqlfilter.Filter'.
|
||||
TODO: Add example of filters.
|
||||
max_retries (Optional[int]): How many times can be event retried.
|
||||
Default value is based on server (3 at the time of this PR).
|
||||
|
||||
Returns:
|
||||
Union[None, dict[str, Any]]: None if there is no event matching
|
||||
|
|
@ -1487,6 +1512,7 @@ class ServerAPI(object):
|
|||
"sourceTopic": source_topic,
|
||||
"targetTopic": target_topic,
|
||||
"sender": sender,
|
||||
"maxRetries": max_retries,
|
||||
}
|
||||
if sequential is not None:
|
||||
kwargs["sequential"] = sequential
|
||||
|
|
@ -2236,6 +2262,34 @@ class ServerAPI(object):
|
|||
response.raise_for_status("Failed to create/update dependency")
|
||||
return response.data
|
||||
|
||||
def _get_dependency_package_route(
|
||||
self, filename=None, platform_name=None
|
||||
):
|
||||
major, minor, patch, _, _ = self.server_version_tuple
|
||||
if (major, minor, patch) <= (0, 2, 0):
|
||||
# Backwards compatibility for AYON server 0.2.0 and lower
|
||||
self.log.warning((
|
||||
"Using deprecated dependency package route."
|
||||
" Please update your AYON server to version 0.2.1 or higher."
|
||||
" Backwards compatibility for this route will be removed"
|
||||
" in future releases of ayon-python-api."
|
||||
))
|
||||
if platform_name is None:
|
||||
platform_name = platform.system().lower()
|
||||
base = "dependencies"
|
||||
if not filename:
|
||||
return base
|
||||
return "{}/{}/{}".format(base, filename, platform_name)
|
||||
|
||||
if (major, minor) <= (0, 3):
|
||||
endpoint = "desktop/dependency_packages"
|
||||
else:
|
||||
endpoint = "desktop/dependencyPackages"
|
||||
|
||||
if filename:
|
||||
return "{}/{}".format(endpoint, filename)
|
||||
return endpoint
|
||||
|
||||
def get_dependency_packages(self):
|
||||
"""Information about dependency packages on server.
|
||||
|
||||
|
|
@ -2263,33 +2317,11 @@ class ServerAPI(object):
|
|||
server.
|
||||
"""
|
||||
|
||||
endpoint = "desktop/dependencyPackages"
|
||||
major, minor, _, _, _ = self.server_version_tuple
|
||||
if major == 0 and minor <= 3:
|
||||
endpoint = "desktop/dependency_packages"
|
||||
|
||||
endpoint = self._get_dependency_package_route()
|
||||
result = self.get(endpoint)
|
||||
result.raise_for_status()
|
||||
return result.data
|
||||
|
||||
def _get_dependency_package_route(
|
||||
self, filename=None, platform_name=None
|
||||
):
|
||||
major, minor, patch, _, _ = self.server_version_tuple
|
||||
if major == 0 and (minor > 2 or (minor == 2 and patch >= 1)):
|
||||
base = "desktop/dependency_packages"
|
||||
if not filename:
|
||||
return base
|
||||
return "{}/{}".format(base, filename)
|
||||
|
||||
# Backwards compatibility for AYON server 0.2.0 and lower
|
||||
if platform_name is None:
|
||||
platform_name = platform.system().lower()
|
||||
base = "dependencies"
|
||||
if not filename:
|
||||
return base
|
||||
return "{}/{}/{}".format(base, filename, platform_name)
|
||||
|
||||
def create_dependency_package(
|
||||
self,
|
||||
filename,
|
||||
|
|
@ -3515,7 +3547,9 @@ class ServerAPI(object):
|
|||
folder_ids=None,
|
||||
folder_paths=None,
|
||||
folder_names=None,
|
||||
folder_types=None,
|
||||
parent_ids=None,
|
||||
statuses=None,
|
||||
active=True,
|
||||
fields=None,
|
||||
own_attributes=False
|
||||
|
|
@ -3536,8 +3570,12 @@ class ServerAPI(object):
|
|||
for filtering.
|
||||
folder_names (Optional[Iterable[str]]): Folder names used
|
||||
for filtering.
|
||||
folder_types (Optional[Iterable[str]]): Folder types used
|
||||
for filtering.
|
||||
parent_ids (Optional[Iterable[str]]): Ids of folder parents.
|
||||
Use 'None' if folder is direct child of project.
|
||||
statuses (Optional[Iterable[str]]): Folder statuses used
|
||||
for filtering.
|
||||
active (Optional[bool]): Filter active/inactive folders.
|
||||
Both are returned if is set to None.
|
||||
fields (Optional[Iterable[str]]): Fields to be queried for
|
||||
|
|
@ -3574,6 +3612,18 @@ class ServerAPI(object):
|
|||
return
|
||||
filters["folderNames"] = list(folder_names)
|
||||
|
||||
if folder_types is not None:
|
||||
folder_types = set(folder_types)
|
||||
if not folder_types:
|
||||
return
|
||||
filters["folderTypes"] = list(folder_types)
|
||||
|
||||
if statuses is not None:
|
||||
statuses = set(statuses)
|
||||
if not statuses:
|
||||
return
|
||||
filters["folderStatuses"] = list(statuses)
|
||||
|
||||
if parent_ids is not None:
|
||||
parent_ids = set(parent_ids)
|
||||
if not parent_ids:
|
||||
|
|
@ -4312,9 +4362,6 @@ class ServerAPI(object):
|
|||
fields.remove("attrib")
|
||||
fields |= self.get_attributes_fields_for_type("version")
|
||||
|
||||
if active is not None:
|
||||
fields.add("active")
|
||||
|
||||
# Make sure fields have minimum required fields
|
||||
fields |= {"id", "version"}
|
||||
|
||||
|
|
@ -4323,6 +4370,9 @@ class ServerAPI(object):
|
|||
use_rest = True
|
||||
fields = {"id"}
|
||||
|
||||
if active is not None:
|
||||
fields.add("active")
|
||||
|
||||
if own_attributes:
|
||||
fields.add("ownAttrib")
|
||||
|
||||
|
|
@ -5845,19 +5895,22 @@ class ServerAPI(object):
|
|||
"""Helper method to get links from server for entity types.
|
||||
|
||||
Example output:
|
||||
[
|
||||
{
|
||||
"id": "59a212c0d2e211eda0e20242ac120002",
|
||||
"linkType": "reference",
|
||||
"description": "reference link between folders",
|
||||
"projectName": "my_project",
|
||||
"author": "frantadmin",
|
||||
"entityId": "b1df109676db11ed8e8c6c9466b19aa8",
|
||||
"entityType": "folder",
|
||||
"direction": "out"
|
||||
},
|
||||
{
|
||||
"59a212c0d2e211eda0e20242ac120001": [
|
||||
{
|
||||
"id": "59a212c0d2e211eda0e20242ac120002",
|
||||
"linkType": "reference",
|
||||
"description": "reference link between folders",
|
||||
"projectName": "my_project",
|
||||
"author": "frantadmin",
|
||||
"entityId": "b1df109676db11ed8e8c6c9466b19aa8",
|
||||
"entityType": "folder",
|
||||
"direction": "out"
|
||||
},
|
||||
...
|
||||
],
|
||||
...
|
||||
]
|
||||
}
|
||||
|
||||
Args:
|
||||
project_name (str): Project where links are.
|
||||
|
|
|
|||
83
openpype/vendor/python/common/ayon_api/utils.py
vendored
83
openpype/vendor/python/common/ayon_api/utils.py
vendored
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
import re
|
||||
import datetime
|
||||
import uuid
|
||||
|
|
@ -15,6 +16,7 @@ except ImportError:
|
|||
import requests
|
||||
import unidecode
|
||||
|
||||
from .constants import SERVER_TIMEOUT_ENV_KEY
|
||||
from .exceptions import UrlError
|
||||
|
||||
REMOVED_VALUE = object()
|
||||
|
|
@ -27,6 +29,23 @@ RepresentationParents = collections.namedtuple(
|
|||
)
|
||||
|
||||
|
||||
def get_default_timeout():
|
||||
"""Default value for requests timeout.
|
||||
|
||||
First looks for environment variable SERVER_TIMEOUT_ENV_KEY which
|
||||
can affect timeout value. If not available then use 10.0 s.
|
||||
|
||||
Returns:
|
||||
float: Timeout value in seconds.
|
||||
"""
|
||||
|
||||
try:
|
||||
return float(os.environ.get(SERVER_TIMEOUT_ENV_KEY))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
return 10.0
|
||||
|
||||
|
||||
class ThumbnailContent:
|
||||
"""Wrapper for thumbnail content.
|
||||
|
||||
|
|
@ -231,30 +250,36 @@ def _try_parse_url(url):
|
|||
return None
|
||||
|
||||
|
||||
def _try_connect_to_server(url):
|
||||
def _try_connect_to_server(url, timeout=None):
|
||||
if timeout is None:
|
||||
timeout = get_default_timeout()
|
||||
try:
|
||||
# TODO add validation if the url lead to Ayon server
|
||||
# - thiw won't validate if the url lead to 'google.com'
|
||||
requests.get(url)
|
||||
# - this won't validate if the url lead to 'google.com'
|
||||
requests.get(url, timeout=timeout)
|
||||
|
||||
except BaseException:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def login_to_server(url, username, password):
|
||||
def login_to_server(url, username, password, timeout=None):
|
||||
"""Use login to the server to receive token.
|
||||
|
||||
Args:
|
||||
url (str): Server url.
|
||||
username (str): User's username.
|
||||
password (str): User's password.
|
||||
timeout (Optional[float]): Timeout for request. Value from
|
||||
'get_default_timeout' is used if not specified.
|
||||
|
||||
Returns:
|
||||
Union[str, None]: User's token if login was successfull.
|
||||
Otherwise 'None'.
|
||||
"""
|
||||
|
||||
if timeout is None:
|
||||
timeout = get_default_timeout()
|
||||
headers = {"Content-Type": "application/json"}
|
||||
response = requests.post(
|
||||
"{}/api/auth/login".format(url),
|
||||
|
|
@ -262,7 +287,8 @@ def login_to_server(url, username, password):
|
|||
json={
|
||||
"name": username,
|
||||
"password": password
|
||||
}
|
||||
},
|
||||
timeout=timeout,
|
||||
)
|
||||
token = None
|
||||
# 200 - success
|
||||
|
|
@ -273,47 +299,67 @@ def login_to_server(url, username, password):
|
|||
return token
|
||||
|
||||
|
||||
def logout_from_server(url, token):
|
||||
def logout_from_server(url, token, timeout=None):
|
||||
"""Logout from server and throw token away.
|
||||
|
||||
Args:
|
||||
url (str): Url from which should be logged out.
|
||||
token (str): Token which should be used to log out.
|
||||
timeout (Optional[float]): Timeout for request. Value from
|
||||
'get_default_timeout' is used if not specified.
|
||||
"""
|
||||
|
||||
if timeout is None:
|
||||
timeout = get_default_timeout()
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer {}".format(token)
|
||||
}
|
||||
requests.post(
|
||||
url + "/api/auth/logout",
|
||||
headers=headers
|
||||
headers=headers,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
|
||||
def is_token_valid(url, token):
|
||||
def is_token_valid(url, token, timeout=None):
|
||||
"""Check if token is valid.
|
||||
|
||||
Token can be a user token or service api key.
|
||||
|
||||
Args:
|
||||
url (str): Server url.
|
||||
token (str): User's token.
|
||||
timeout (Optional[float]): Timeout for request. Value from
|
||||
'get_default_timeout' is used if not specified.
|
||||
|
||||
Returns:
|
||||
bool: True if token is valid.
|
||||
"""
|
||||
|
||||
headers = {
|
||||
if timeout is None:
|
||||
timeout = get_default_timeout()
|
||||
|
||||
base_headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer {}".format(token)
|
||||
}
|
||||
response = requests.get(
|
||||
"{}/api/users/me".format(url),
|
||||
headers=headers
|
||||
)
|
||||
return response.status_code == 200
|
||||
for header_value in (
|
||||
{"Authorization": "Bearer {}".format(token)},
|
||||
{"X-Api-Key": token},
|
||||
):
|
||||
headers = base_headers.copy()
|
||||
headers.update(header_value)
|
||||
response = requests.get(
|
||||
"{}/api/users/me".format(url),
|
||||
headers=headers,
|
||||
timeout=timeout,
|
||||
)
|
||||
if response.status_code == 200:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def validate_url(url):
|
||||
def validate_url(url, timeout=None):
|
||||
"""Validate url if is valid and server is available.
|
||||
|
||||
Validation checks if can be parsed as url and contains scheme.
|
||||
|
|
@ -334,6 +380,7 @@ def validate_url(url):
|
|||
|
||||
Args:
|
||||
url (str): Server url.
|
||||
timeout (Optional[int]): Timeout in seconds for connection to server.
|
||||
|
||||
Returns:
|
||||
Url which was used to connect to server.
|
||||
|
|
@ -369,10 +416,10 @@ def validate_url(url):
|
|||
# - this will trigger UrlError if both will crash
|
||||
if not parsed_url.scheme:
|
||||
new_url = "https://" + modified_url
|
||||
if _try_connect_to_server(new_url):
|
||||
if _try_connect_to_server(new_url, timeout=timeout):
|
||||
return new_url
|
||||
|
||||
if _try_connect_to_server(modified_url):
|
||||
if _try_connect_to_server(modified_url, timeout=timeout):
|
||||
return modified_url
|
||||
|
||||
hints = []
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
"""Package declaring Python API for Ayon server."""
|
||||
__version__ = "0.4.1"
|
||||
__version__ = "0.5.1"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue