11from  __future__ import  annotations 
22
3+ from  collections .abc  import  Iterable 
34from  copy  import  copy 
45from  typing  import  TYPE_CHECKING , Any 
56
67from  ..constants  import  InfrahubClientMode 
7- from  ..exceptions  import  FeatureNotSupportedError , NodeNotFoundError , SchemaNotFoundError 
8+ from  ..exceptions  import  FeatureNotSupportedError , NodeNotFoundError , ResourceNotDefinedError ,  SchemaNotFoundError 
89from  ..graphql  import  Mutation , Query 
910from  ..schema  import  GenericSchemaAPI , RelationshipCardinality , RelationshipKind 
1011from  ..utils  import  compare_lists , generate_short_id , get_flat_value 
@@ -42,8 +43,6 @@ def __init__(self, schema: MainSchemaTypesAPI, branch: str, data: dict | None =
4243        self ._branch  =  branch 
4344        self ._existing : bool  =  True 
4445        self ._attribute_data : dict [str , Attribute ] =  {}
45-         self ._relationship_cardinality_many_data : dict [str , RelationshipManager  |  RelationshipManagerSync ] =  {}
46-         self ._relationship_cardinality_one_data : dict [str , RelatedNode  |  RelatedNodeSync ] =  {}
4746
4847        # Generate a unique ID only to be used inside the SDK 
4948        # The format if this ID is purposely different from the ID used by the API 
@@ -142,18 +141,6 @@ def _init_attributes(self, data: dict | None = None) -> None:
142141                name = attr_schema .name , schema = attr_schema , data = attr_data 
143142            )
144143
145-     def  __getattr__ (
146-         self , name : str 
147-     ) ->  Attribute  |  RelationshipManager  |  RelationshipManagerSync  |  RelatedNode  |  RelatedNodeSync :
148-         if  "_attribute_data"  in  self .__dict__  and  name  in  self ._attribute_data :
149-             return  self ._attribute_data [name ]
150-         if  "_relationship_cardinality_many_data"  in  self .__dict__  and  name  in  self ._relationship_cardinality_many_data :
151-             return  self ._relationship_cardinality_many_data [name ]
152-         if  "_relationship_cardinality_one_data"  in  self .__dict__  and  name  in  self ._relationship_cardinality_one_data :
153-             return  self ._relationship_cardinality_one_data [name ]
154- 
155-         raise  AttributeError (f"'{ self .__class__ .__name__ } { name }  )
156- 
157144    def  __setattr__ (self , name : str , value : Any ) ->  None :
158145        """Set values for attributes that exist or revert to normal behaviour""" 
159146        if  "_attribute_data"  in  self .__dict__  and  name  in  self ._attribute_data :
@@ -463,6 +450,12 @@ def _relationship_mutation(self, action: str, relation_to_update: str, related_n
463450        }} 
464451        """ 
465452
453+     def  _get_attribute (self , name : str ) ->  Attribute :
454+         if  name  in  self ._attribute_data :
455+             return  self ._attribute_data [name ]
456+ 
457+         raise  ResourceNotDefinedError (message = f"The node doesn't have an attribute for { name }  )
458+ 
466459
467460class  InfrahubNode (InfrahubNodeBase ):
468461    """Represents a Infrahub node in an asynchronous context.""" 
@@ -486,6 +479,9 @@ def __init__(
486479        if  isinstance (data , dict ) and  isinstance (data .get ("node" ), dict ):
487480            data  =  data .get ("node" )
488481
482+         self ._relationship_cardinality_many_data : dict [str , RelationshipManager ] =  {}
483+         self ._relationship_cardinality_one_data : dict [str , RelatedNode ] =  {}
484+ 
489485        super ().__init__ (schema = schema , branch = branch  or  client .default_branch , data = data )
490486
491487    @classmethod  
@@ -523,6 +519,16 @@ def _init_relationships(self, data: dict | None = None) -> None:
523519                    data = rel_data ,
524520                )
525521
522+     def  __getattr__ (self , name : str ) ->  Attribute  |  RelationshipManager  |  RelatedNode :
523+         if  "_attribute_data"  in  self .__dict__  and  name  in  self ._attribute_data :
524+             return  self ._attribute_data [name ]
525+         if  "_relationship_cardinality_many_data"  in  self .__dict__  and  name  in  self ._relationship_cardinality_many_data :
526+             return  self ._relationship_cardinality_many_data [name ]
527+         if  "_relationship_cardinality_one_data"  in  self .__dict__  and  name  in  self ._relationship_cardinality_one_data :
528+             return  self ._relationship_cardinality_one_data [name ]
529+ 
530+         raise  AttributeError (f"'{ self .__class__ .__name__ } { name }  )
531+ 
526532    def  __setattr__ (self , name : str , value : Any ) ->  None :
527533        """Set values for relationship names that exist or revert to normal behaviour""" 
528534        if  "_relationship_cardinality_one_data"  in  self .__dict__  and  name  in  self ._relationship_cardinality_one_data :
@@ -552,14 +558,14 @@ async def artifact_generate(self, name: str) -> None:
552558        self ._validate_artifact_support (ARTIFACT_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE )
553559
554560        artifact  =  await  self ._client .get (kind = "CoreArtifact" , name__value = name , object__ids = [self .id ])
555-         await  artifact .definition .fetch ()   # type: ignore[attr-defined] 
556-         await  artifact .definition .peer .generate ([artifact .id ])   # type: ignore[attr-defined] 
561+         await  artifact ._get_relationship_one ( name = " definition" ) .fetch ()
562+         await  artifact ._get_relationship_one ( name = " definition" ) .peer .generate ([artifact .id ])
557563
558564    async  def  artifact_fetch (self , name : str ) ->  str  |  dict [str , Any ]:
559565        self ._validate_artifact_support (ARTIFACT_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE )
560566
561567        artifact  =  await  self ._client .get (kind = "CoreArtifact" , name__value = name , object__ids = [self .id ])
562-         content  =  await  self ._client .object_store .get (identifier = artifact .storage_id .value )   # type: ignore[attr-defined] 
568+         content  =  await  self ._client .object_store .get (identifier = artifact ._get_attribute ( name = " storage_id" ) .value )
563569        return  content 
564570
565571    async  def  delete (self , timeout : int  |  None  =  None , request_context : RequestContext  |  None  =  None ) ->  None :
@@ -1002,6 +1008,27 @@ async def get_pool_resources_utilization(self) -> list[dict[str, Any]]:
10021008            return  [edge ["node" ] for  edge  in  response [graphql_query_name ]["edges" ]]
10031009        return  []
10041010
1011+     def  _get_relationship_many (self , name : str ) ->  RelationshipManager :
1012+         if  name  in  self ._relationship_cardinality_many_data :
1013+             return  self ._relationship_cardinality_many_data [name ]
1014+ 
1015+         raise  ResourceNotDefinedError (message = f"The node doesn't have a cardinality=many relationship for { name }  )
1016+ 
1017+     def  _get_relationship_one (self , name : str ) ->  RelatedNode :
1018+         if  name  in  self ._relationship_cardinality_one_data :
1019+             return  self ._relationship_cardinality_one_data [name ]
1020+ 
1021+         raise  ResourceNotDefinedError (message = f"The node doesn't have a cardinality=one relationship for { name }  )
1022+ 
1023+     def  __dir__ (self ) ->  Iterable [str ]:
1024+         base  =  list (super ().__dir__ ())
1025+         return  sorted (
1026+             base 
1027+             +  list (self ._attribute_data .keys ())
1028+             +  list (self ._relationship_cardinality_many_data .keys ())
1029+             +  list (self ._relationship_cardinality_one_data .keys ())
1030+         )
1031+ 
10051032
10061033class  InfrahubNodeSync (InfrahubNodeBase ):
10071034    """Represents a Infrahub node in a synchronous context.""" 
@@ -1025,6 +1052,9 @@ def __init__(
10251052        if  isinstance (data , dict ) and  isinstance (data .get ("node" ), dict ):
10261053            data  =  data .get ("node" )
10271054
1055+         self ._relationship_cardinality_many_data : dict [str , RelationshipManagerSync ] =  {}
1056+         self ._relationship_cardinality_one_data : dict [str , RelatedNodeSync ] =  {}
1057+ 
10281058        super ().__init__ (schema = schema , branch = branch  or  client .default_branch , data = data )
10291059
10301060    @classmethod  
@@ -1063,6 +1093,16 @@ def _init_relationships(self, data: dict | None = None) -> None:
10631093                    data = rel_data ,
10641094                )
10651095
1096+     def  __getattr__ (self , name : str ) ->  Attribute  |  RelationshipManagerSync  |  RelatedNodeSync :
1097+         if  "_attribute_data"  in  self .__dict__  and  name  in  self ._attribute_data :
1098+             return  self ._attribute_data [name ]
1099+         if  "_relationship_cardinality_many_data"  in  self .__dict__  and  name  in  self ._relationship_cardinality_many_data :
1100+             return  self ._relationship_cardinality_many_data [name ]
1101+         if  "_relationship_cardinality_one_data"  in  self .__dict__  and  name  in  self ._relationship_cardinality_one_data :
1102+             return  self ._relationship_cardinality_one_data [name ]
1103+ 
1104+         raise  AttributeError (f"'{ self .__class__ .__name__ } { name }  )
1105+ 
10661106    def  __setattr__ (self , name : str , value : Any ) ->  None :
10671107        """Set values for relationship names that exist or revert to normal behaviour""" 
10681108        if  "_relationship_cardinality_one_data"  in  self .__dict__  and  name  in  self ._relationship_cardinality_one_data :
@@ -1090,13 +1130,13 @@ def generate(self, nodes: list[str] | None = None) -> None:
10901130    def  artifact_generate (self , name : str ) ->  None :
10911131        self ._validate_artifact_support (ARTIFACT_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE )
10921132        artifact  =  self ._client .get (kind = "CoreArtifact" , name__value = name , object__ids = [self .id ])
1093-         artifact .definition .fetch ()   # type: ignore[attr-defined] 
1094-         artifact .definition .peer .generate ([artifact .id ])   # type: ignore[attr-defined] 
1133+         artifact ._get_relationship_one ( name = " definition" ) .fetch ()
1134+         artifact ._get_relationship_one ( name = " definition" ) .peer .generate ([artifact .id ])
10951135
10961136    def  artifact_fetch (self , name : str ) ->  str  |  dict [str , Any ]:
10971137        self ._validate_artifact_support (ARTIFACT_FETCH_FEATURE_NOT_SUPPORTED_MESSAGE )
10981138        artifact  =  self ._client .get (kind = "CoreArtifact" , name__value = name , object__ids = [self .id ])
1099-         content  =  self ._client .object_store .get (identifier = artifact .storage_id .value )   # type: ignore[attr-defined] 
1139+         content  =  self ._client .object_store .get (identifier = artifact ._get_attribute ( name = " storage_id" ) .value )
11001140        return  content 
11011141
11021142    def  delete (self , timeout : int  |  None  =  None , request_context : RequestContext  |  None  =  None ) ->  None :
@@ -1538,3 +1578,24 @@ def get_pool_resources_utilization(self) -> list[dict[str, Any]]:
15381578        if  response [graphql_query_name ].get ("count" , 0 ):
15391579            return  [edge ["node" ] for  edge  in  response [graphql_query_name ]["edges" ]]
15401580        return  []
1581+ 
1582+     def  _get_relationship_many (self , name : str ) ->  RelationshipManager  |  RelationshipManagerSync :
1583+         if  name  in  self ._relationship_cardinality_many_data :
1584+             return  self ._relationship_cardinality_many_data [name ]
1585+ 
1586+         raise  ResourceNotDefinedError (message = f"The node doesn't have a cardinality=many relationship for { name }  )
1587+ 
1588+     def  _get_relationship_one (self , name : str ) ->  RelatedNode  |  RelatedNodeSync :
1589+         if  name  in  self ._relationship_cardinality_one_data :
1590+             return  self ._relationship_cardinality_one_data [name ]
1591+ 
1592+         raise  ResourceNotDefinedError (message = f"The node doesn't have a cardinality=one relationship for { name }  )
1593+ 
1594+     def  __dir__ (self ) ->  Iterable [str ]:
1595+         base  =  list (super ().__dir__ ())
1596+         return  sorted (
1597+             base 
1598+             +  list (self ._attribute_data .keys ())
1599+             +  list (self ._relationship_cardinality_many_data .keys ())
1600+             +  list (self ._relationship_cardinality_one_data .keys ())
1601+         )
0 commit comments