From 6d6530c63c9f4bc8d412ba700d1e2239bd9cc7ea Mon Sep 17 00:00:00 2001 From: Martastain Date: Fri, 2 Aug 2024 15:08:19 +0200 Subject: [PATCH 1/3] feat: add error field to ResolvedURIModel --- api/resolve/models.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/api/resolve/models.py b/api/resolve/models.py index 6e2f49cb..c8c5abd4 100644 --- a/api/resolve/models.py +++ b/api/resolve/models.py @@ -73,8 +73,8 @@ class ResolvedURIModel(OPModel): title="Resolved URI", example="ayon+entity://demo_Big_Feature/assets/environments/01_pfueghtiaoft?product=layoutMain&version=v004&representation=ma", ) - entities: list[ResolvedEntityModel] = Field( - ..., + entities: list[ResolvedEntityModel] | None = Field( + None, title="Resolved entities", example=[ { @@ -89,6 +89,11 @@ class ResolvedURIModel(OPModel): } ], ) + error: str | None = Field( + None, + title="Error", + description="Error message if the URI could not be resolved", + ) class ParsedURIModel(OPModel): From 02e3f29fe9a897e2b71b5dfb0a58b9864a0f4b44 Mon Sep 17 00:00:00 2001 From: Martastain Date: Fri, 2 Aug 2024 15:08:44 +0200 Subject: [PATCH 2/3] feat: resolve endpoint error handling per entity --- api/resolve/__init__.py | 63 +++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/api/resolve/__init__.py b/api/resolve/__init__.py index 661b4a05..2d22e207 100644 --- a/api/resolve/__init__.py +++ b/api/resolve/__init__.py @@ -4,7 +4,6 @@ from urllib.parse import parse_qs, urlparse from fastapi import APIRouter, Query -from nxtools import logging from ayon_server.api.dependencies import ClientSiteID, CurrentUser from ayon_server.exceptions import BadRequestException, ServiceUnavailableException @@ -24,6 +23,7 @@ SDF_REGEX = re.compile(r":SDF_FORMAT_ARGS.*$") +NAME_VALIDATOR = re.compile(NAME_REGEX) def sanitize_uri(uri: str) -> str: @@ -37,8 +37,8 @@ def validate_name(name: str | None) -> None: return if name == "*": return - name_validator = re.compile(NAME_REGEX) - assert name_validator.match(name), f"Invalid name: {name}" + if not NAME_VALIDATOR.match(name): + raise ValueError(f"Invalid name: {name}") def parse_uri(uri: str) -> ParsedURIModel: @@ -53,16 +53,18 @@ def parse_uri(uri: str) -> ParsedURIModel: uri = sanitize_uri(uri) parsed_uri = urlparse(uri) - assert parsed_uri.scheme in [ - "ayon", - "ayon+entity", - ], f"Invalid scheme: {parsed_uri.scheme}" + if parsed_uri.scheme not in ["ayon", "ayon+entity"]: + raise ValueError(f"Invalid scheme: {parsed_uri.scheme}") project_name = parsed_uri.netloc - name_validator = re.compile(NAME_REGEX) - assert name_validator.match(project_name), f"Invalid project name: {project_name}" + if not NAME_VALIDATOR.match(project_name): + raise ValueError(f"Invalid project name: {project_name}") path = parsed_uri.path.strip("/") or None + if path: + for element in path.split("/"): + if not NAME_VALIDATOR.match(element): + raise ValueError(f"Invalid path element: {element}") qs: dict[str, Any] = parse_qs(parsed_uri.query) @@ -89,11 +91,14 @@ def parse_uri(uri: str) -> ParsedURIModel: # assert we don't have incompatible arguments if task_name is not None or workfile_name is not None: - assert product_name is None, "Tasks cannot be queried with products" - assert version_name is None, "Tasks cannot be queried with versions" - assert ( - representation_name is None - ), "Tasks cannot be queried with representations" + if product_name is not None: + raise ValueError("Tasks and workfiles cannot be queried with products") + if version_name is not None: + raise ValueError("Tasks and workfiles cannot be queried with versions") + if representation_name is not None: + raise ValueError( + "Tasks and workfiles cannot be queried with representations" + ) return ParsedURIModel( uri=uri, @@ -140,6 +145,7 @@ def get_version_conditions(version_name: str | None) -> list[str]: return [] original_version_name = version_name + version_name = version_name.strip().lower() if version_name.startswith("v"): version_name = version_name[1:] @@ -159,8 +165,7 @@ def get_version_conditions(version_name: str | None) -> list[str]: if version_name == "hero": return ["v.version < 0"] - logging.debug(f"Invalid version name: {original_version_name}") - return ["FALSE"] + raise ValueError(f"Invalid version name: {original_version_name}") def get_representation_conditions(representation_name: str | None) -> list[str]: @@ -357,18 +362,28 @@ async def resolve_uris( async with Postgres.acquire() as conn: async with conn.transaction(): for uri in request.uris: - parsed_uri = parse_uri(uri) + try: + parsed_uri = parse_uri(uri) + except ValueError as e: + result.append(ResolvedURIModel(uri=uri, error=str(e))) + continue + if parsed_uri.project_name != current_project: await conn.execute( f"SET LOCAL search_path TO project_{parsed_uri.project_name}" ) current_project = parsed_uri.project_name - entities = await resolve_entities( - conn, - parsed_uri, - roots.get(current_project, {}), - site_id, - path_only=path_only, - ) + + try: + entities = await resolve_entities( + conn, + parsed_uri, + roots.get(current_project, {}), + site_id, + path_only=path_only, + ) + except ValueError as e: + result.append(ResolvedURIModel(uri=uri, error=str(e))) + continue result.append(ResolvedURIModel(uri=uri, entities=entities)) return result From 16c4e410992ab17a513dc0ee3beda6b515cb868c Mon Sep 17 00:00:00 2001 From: Martastain Date: Wed, 18 Sep 2024 10:32:55 +0200 Subject: [PATCH 3/3] fix: return empty list in the case of error instead of None --- api/resolve/__init__.py | 2 +- api/resolve/models.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/resolve/__init__.py b/api/resolve/__init__.py index 2d22e207..5ff1f16a 100644 --- a/api/resolve/__init__.py +++ b/api/resolve/__init__.py @@ -383,7 +383,7 @@ async def resolve_uris( path_only=path_only, ) except ValueError as e: - result.append(ResolvedURIModel(uri=uri, error=str(e))) + result.append(ResolvedURIModel(uri=uri, entities=[], error=str(e))) continue result.append(ResolvedURIModel(uri=uri, entities=entities)) return result diff --git a/api/resolve/models.py b/api/resolve/models.py index c8c5abd4..9ba3e65c 100644 --- a/api/resolve/models.py +++ b/api/resolve/models.py @@ -73,8 +73,8 @@ class ResolvedURIModel(OPModel): title="Resolved URI", example="ayon+entity://demo_Big_Feature/assets/environments/01_pfueghtiaoft?product=layoutMain&version=v004&representation=ma", ) - entities: list[ResolvedEntityModel] | None = Field( - None, + entities: list[ResolvedEntityModel] = Field( + default_factory=list, title="Resolved entities", example=[ {