11import json
22import os
33from functools import wraps
4+ from pathlib import PurePath
45from typing import Any , Callable , List , Optional , Union
56
67from pydantic import BaseModel , ConfigDict , Field , field_validator
@@ -103,7 +104,7 @@ def convert_folder_type(cls, v: Union[str, int, None]) -> Optional[str]:
103104 return v
104105
105106
106- class ProjectStructure (BaseModel ):
107+ class ProjectStructure (ProjectFolder ):
107108 """Model representing the complete file structure of a UiPath project.
108109
109110 Attributes:
@@ -114,34 +115,7 @@ class ProjectStructure(BaseModel):
114115 folder_type: The type of the root folder (optional)
115116 """
116117
117- model_config = ConfigDict (
118- validate_by_name = True ,
119- validate_by_alias = True ,
120- use_enum_values = True ,
121- arbitrary_types_allowed = True ,
122- extra = "allow" ,
123- )
124-
125- id : Optional [str ] = Field (default = None , alias = "id" )
126- name : Optional [str ] = Field (default = None , alias = "name" )
127- folders : List [ProjectFolder ] = Field (default_factory = list )
128- files : List [ProjectFile ] = Field (default_factory = list )
129- folder_type : Optional [str ] = Field (default = None , alias = "folderType" )
130-
131- @field_validator ("folder_type" , mode = "before" )
132- @classmethod
133- def convert_folder_type (cls , v : Union [str , int , None ]) -> Optional [str ]:
134- """Convert numeric folder type to string.
135-
136- Args:
137- v: The value to convert
138-
139- Returns:
140- Optional[str]: The converted value or None
141- """
142- if isinstance (v , int ):
143- return str (v )
144- return v
118+ pass
145119
146120
147121class LockInfo (BaseModel ):
@@ -174,6 +148,33 @@ def get_folder_by_name(
174148 return None
175149
176150
151+ def resolve_path (
152+ folder : ProjectFolder ,
153+ path : PurePath ,
154+ ) -> ProjectFile | ProjectFolder :
155+ """Resolve a path relative to the folder.
156+
157+ Args:
158+ folder: Project folder
159+ path: Path relative to the folder
160+
161+ Returns: The resolved folder or file. If resolution fails, an assertion is raised.
162+ """
163+ root = path .parts
164+ while len (root ) > 1 :
165+ child = next (
166+ (folder for folder in folder .folders if folder .name == root [0 ]), None
167+ )
168+ assert child , "Path not found."
169+ folder = child
170+ root = root [1 :]
171+ file = next ((f for f in folder .files if f .name == root [0 ]), None )
172+ child = next ((folder for folder in folder .folders if folder .name == root [0 ]), None )
173+ resolved = file or child
174+ assert resolved , "Path not found."
175+ return resolved
176+
177+
177178class AddedResource (BaseModel ):
178179 """Represents a new file to be added during a structural migration."""
179180
0 commit comments