diff --git a/ayon_api/__init__.py b/ayon_api/__init__.py index 0c52eb8ba..fc89a1dfb 100644 --- a/ayon_api/__init__.py +++ b/ayon_api/__init__.py @@ -55,6 +55,7 @@ get_server_version, get_server_version_tuple, is_product_base_type_supported, + links_graphql_support_data, get_users, get_user_by_name, get_user, @@ -339,6 +340,7 @@ "get_server_version", "get_server_version_tuple", "is_product_base_type_supported", + "links_graphql_support_data", "get_users", "get_user_by_name", "get_user", diff --git a/ayon_api/_api.py b/ayon_api/_api.py index 97833c8c8..a00bb04c0 100644 --- a/ayon_api/_api.py +++ b/ayon_api/_api.py @@ -729,6 +729,13 @@ def is_product_base_type_supported() -> bool: return con.is_product_base_type_supported() +def links_graphql_support_data() -> bool: + """Links data can be received by GraphQl. + """ + con = get_server_api_connection() + return con.links_graphql_support_data() + + def get_users( project_name: Optional[str] = None, usernames: Optional[Iterable[str]] = None, diff --git a/ayon_api/_api_helpers/base.py b/ayon_api/_api_helpers/base.py index 0a3d19550..8ef198c68 100644 --- a/ayon_api/_api_helpers/base.py +++ b/ayon_api/_api_helpers/base.py @@ -28,6 +28,9 @@ def log(self) -> logging.Logger: def is_product_base_type_supported(self) -> bool: raise NotImplementedError() + def links_graphql_support_data(self) -> bool: + raise NotImplementedError() + def get_server_version(self) -> str: raise NotImplementedError() @@ -142,6 +145,9 @@ def _prepare_fields( ): raise NotImplementedError() + def _prepare_link_fields(self, fields: set[str]) -> None: + raise NotImplementedError() + def _prepare_advanced_filters( self, filters: Union[str, dict[str, Any], None] ) -> Optional[str]: diff --git a/ayon_api/_api_helpers/folders.py b/ayon_api/_api_helpers/folders.py index 71535bdd5..d10cfeb7a 100644 --- a/ayon_api/_api_helpers/folders.py +++ b/ayon_api/_api_helpers/folders.py @@ -331,6 +331,8 @@ def get_folders( if own_attributes: fields.add("ownAttrib") + self._prepare_link_fields(fields) + query = folders_graphql_query(fields) for attr, filter_value in graphql_filters.items(): query.set_variable_value(attr, filter_value) diff --git a/ayon_api/_api_helpers/links.py b/ayon_api/_api_helpers/links.py index dc73f7890..b2b8d0973 100644 --- a/ayon_api/_api_helpers/links.py +++ b/ayon_api/_api_helpers/links.py @@ -362,6 +362,7 @@ def get_entities_links( return output link_fields = {"id", "links"} + self._prepare_link_fields(link_fields) query = query_func(link_fields) for attr, filter_value in filters.items(): query.set_variable_value(attr, filter_value) diff --git a/ayon_api/_api_helpers/products.py b/ayon_api/_api_helpers/products.py index db1e29a0c..2eacb7cd7 100644 --- a/ayon_api/_api_helpers/products.py +++ b/ayon_api/_api_helpers/products.py @@ -179,6 +179,8 @@ def get_products( if filter_value: graphql_filters[filter_key] = filter_value + self._prepare_link_fields(fields) + query = products_graphql_query(fields) for attr, filter_value in graphql_filters.items(): query.set_variable_value(attr, filter_value) diff --git a/ayon_api/_api_helpers/representations.py b/ayon_api/_api_helpers/representations.py index 56ac8e372..4535fa633 100644 --- a/ayon_api/_api_helpers/representations.py +++ b/ayon_api/_api_helpers/representations.py @@ -108,6 +108,8 @@ def get_representations( fields.discard("files") fields |= REPRESENTATION_FILES_FIELDS + self._prepare_link_fields(fields) + graphql_filters = { "projectName": project_name } diff --git a/ayon_api/_api_helpers/tasks.py b/ayon_api/_api_helpers/tasks.py index 4109d72ed..4ffa2dd5f 100644 --- a/ayon_api/_api_helpers/tasks.py +++ b/ayon_api/_api_helpers/tasks.py @@ -106,6 +106,8 @@ def get_tasks( if active is not None: fields.add("active") + self._prepare_link_fields(fields) + query = tasks_graphql_query(fields) for attr, filter_value in graphql_filters.items(): query.set_variable_value(attr, filter_value) @@ -267,6 +269,8 @@ def get_tasks_by_folder_paths( if active is not None: fields.add("active") + self._prepare_link_fields(fields) + query = tasks_by_folder_paths_graphql_query(fields) for attr, filter_value in graphql_filters.items(): query.set_variable_value(attr, filter_value) diff --git a/ayon_api/_api_helpers/versions.py b/ayon_api/_api_helpers/versions.py index 97800451c..8a355fca1 100644 --- a/ayon_api/_api_helpers/versions.py +++ b/ayon_api/_api_helpers/versions.py @@ -87,6 +87,8 @@ def get_versions( if active is not None: fields.add("active") + self._prepare_link_fields(fields) + if own_attributes is not _PLACEHOLDER: warnings.warn( ( @@ -116,7 +118,6 @@ def get_versions( ): return - filters = self._prepare_advanced_filters(filters) if filters: graphql_filters["filter"] = filters diff --git a/ayon_api/_api_helpers/workfiles.py b/ayon_api/_api_helpers/workfiles.py index a0e35b9a8..67991da54 100644 --- a/ayon_api/_api_helpers/workfiles.py +++ b/ayon_api/_api_helpers/workfiles.py @@ -93,6 +93,8 @@ def get_workfile_entities( fields = set(fields) self._prepare_fields("workfile", fields) + self._prepare_link_fields(fields) + query = workfiles_info_graphql_query(fields) for attr, filter_value in filters.items(): diff --git a/ayon_api/graphql_queries.py b/ayon_api/graphql_queries.py index df0a1e69e..27719d389 100644 --- a/ayon_api/graphql_queries.py +++ b/ayon_api/graphql_queries.py @@ -1,18 +1,31 @@ +from __future__ import annotations + import collections +import typing from .constants import DEFAULT_LINK_FIELDS from .graphql import FIELD_VALUE, GraphQlQuery, fields_to_dict +if typing.TYPE_CHECKING: + from .graphql import ( + GraphQlQueryEdgeField, + ) + -def add_links_fields(entity_field, nested_fields): +def add_links_fields( + entity_field: GraphQlQueryEdgeField, + nested_fields: dict | None, +) -> None: if "links" not in nested_fields: return links_fields = nested_fields.pop("links") - link_edge_fields = set(DEFAULT_LINK_FIELDS) + if isinstance(links_fields, dict): simple_fields = set(links_fields) - simple_variant = len(simple_fields - link_edge_fields) == 0 + diff = simple_fields - link_edge_fields + diff.discard("data") + simple_variant = len(diff) == 0 else: simple_variant = True simple_fields = link_edge_fields @@ -121,7 +134,7 @@ def product_types_query(fields): return query -def folders_graphql_query(fields): +def folders_graphql_query(fields: set[str]) -> GraphQlQuery: query = GraphQlQuery("FoldersQuery") project_name_var = query.add_variable("projectName", "String!") folder_ids_var = query.add_variable("folderIds", "[String!]") @@ -161,6 +174,7 @@ def folders_graphql_query(fields): folders_field.set_filter("filter", filter_var) nested_fields = fields_to_dict(fields) + add_links_fields(folders_field, nested_fields) query_queue = collections.deque() @@ -179,7 +193,7 @@ def folders_graphql_query(fields): return query -def tasks_graphql_query(fields): +def tasks_graphql_query(fields: set[str]) -> GraphQlQuery: query = GraphQlQuery("TasksQuery") project_name_var = query.add_variable("projectName", "String!") task_ids_var = query.add_variable("taskIds", "[String!]") @@ -227,7 +241,7 @@ def tasks_graphql_query(fields): return query -def tasks_by_folder_paths_graphql_query(fields): +def tasks_by_folder_paths_graphql_query(fields: set[str]) -> GraphQlQuery: query = GraphQlQuery("TasksByFolderPathQuery") project_name_var = query.add_variable("projectName", "String!") task_names_var = query.add_variable("taskNames", "[String!]") @@ -258,6 +272,7 @@ def tasks_by_folder_paths_graphql_query(fields): tasks_field.set_filter("filter", filter_var) nested_fields = fields_to_dict(fields) + add_links_fields(tasks_field, nested_fields) query_queue = collections.deque() @@ -276,7 +291,7 @@ def tasks_by_folder_paths_graphql_query(fields): return query -def products_graphql_query(fields): +def products_graphql_query(fields: set[str]) -> GraphQlQuery: query = GraphQlQuery("ProductsQuery") project_name_var = query.add_variable("projectName", "String!") @@ -326,7 +341,7 @@ def products_graphql_query(fields): return query -def versions_graphql_query(fields): +def versions_graphql_query(fields: set[str]) -> GraphQlQuery: query = GraphQlQuery("VersionsQuery") project_name_var = query.add_variable("projectName", "String!") @@ -377,7 +392,7 @@ def versions_graphql_query(fields): return query -def representations_graphql_query(fields): +def representations_graphql_query(fields: set[str]) -> GraphQlQuery: query = GraphQlQuery("RepresentationsQuery") project_name_var = query.add_variable("projectName", "String!") @@ -525,7 +540,7 @@ def representations_hierarchy_qraphql_query( return query -def workfiles_info_graphql_query(fields): +def workfiles_info_graphql_query(fields: set[str]) -> GraphQlQuery: query = GraphQlQuery("WorkfilesInfo") project_name_var = query.add_variable("projectName", "String!") workfiles_info_ids = query.add_variable("workfileIds", "[String!]") diff --git a/ayon_api/server_api.py b/ayon_api/server_api.py index b97e39f18..ab3a3d1f0 100644 --- a/ayon_api/server_api.py +++ b/ayon_api/server_api.py @@ -38,6 +38,7 @@ DEFAULT_ACTIVITY_FIELDS, DEFAULT_USER_FIELDS, DEFAULT_ENTITY_LIST_FIELDS, + DEFAULT_LINK_FIELDS, ) from .graphql import INTROSPECTION_QUERY from .graphql_queries import users_graphql_query @@ -329,6 +330,7 @@ def __init__( self._graphql_allows_traits_in_representations: Optional[bool] = None self._product_base_type_supported = None + self._links_graphql_support_data = None self._session = None @@ -922,6 +924,15 @@ def is_product_base_type_supported(self) -> bool: ) return self._product_base_type_supported + def links_graphql_support_data(self) -> bool: + """Links data can be received by GraphQl.""" + if self._links_graphql_support_data is None: + major, minor, patch, _, _ = self.server_version_tuple + self._links_graphql_support_data = ( + (major, minor, patch) >= (1, 14, 2) + ) + return self._links_graphql_support_data + def _get_user_info(self) -> Optional[dict[str, Any]]: if self._access_token is None: return None @@ -2445,6 +2456,17 @@ def _prepare_fields( ) } + def _prepare_link_fields(self, fields: set[str]) -> None: + if "links" not in fields: + return + + fields.discard("links") + for field in DEFAULT_LINK_FIELDS: + fields.add(f"links.{field}") + + if self.links_graphql_support_data(): + fields.add("links.data") + def _prepare_advanced_filters( self, filters: Union[str, dict[str, Any], None] ) -> Optional[str]: