diff --git a/backend/infrahub/core/manager.py b/backend/infrahub/core/manager.py index 84256a3fe1..0101a62486 100644 --- a/backend/infrahub/core/manager.py +++ b/backend/infrahub/core/manager.py @@ -400,7 +400,7 @@ async def query_peers( results = [] for peer in peers_info: - result = await Relationship(schema=schema, branch=branch, at=at, node_id=peer.source_id).load( + result = Relationship(schema=schema, branch=branch, at=at, node_id=peer.source_id).load( db=db, id=peer.rel_node_id, db_id=peer.rel_node_db_id, @@ -408,7 +408,7 @@ async def query_peers( data=peer, ) if fetch_peers: - await result.set_peer(value=peer_nodes[peer.peer_id]) + result.set_peer(value=peer_nodes[peer.peer_id]) results.append(result) return results diff --git a/backend/infrahub/core/query/node.py b/backend/infrahub/core/query/node.py index 91ff3c2f68..560c23fd6c 100644 --- a/backend/infrahub/core/query/node.py +++ b/backend/infrahub/core/query/node.py @@ -157,7 +157,20 @@ async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG relationships: list[RelationshipCreateData] = [] for rel_name in self.node._relationships: rel_manager: RelationshipManager = getattr(self.node, rel_name) + # Fetch all relationship peers through a single database call for performances. + peers = await rel_manager.get_peers(db=db, branch_agnostic=self.branch_agnostic) + for rel in rel_manager._relationships: + try: + rel.set_peer(value=peers[rel.get_peer_id()]) + except KeyError: + pass + except ValueError: + # Relationship has not been initialized yet, it means the peer does not exist in db yet + # typically because it will be allocated from a ressource pool. In that case, the peer + # will be fetched using `rel.resolve` later. + pass + rel_create_data = await rel.get_create_data(db=db, at=at) if rel_create_data.peer_branch_level > deepest_branch_level or ( deepest_branch_name == GLOBAL_BRANCH_NAME and rel_create_data.peer_branch == registry.default_branch diff --git a/backend/infrahub/core/relationship/model.py b/backend/infrahub/core/relationship/model.py index bce7b1bd85..7715c0a4ab 100644 --- a/backend/infrahub/core/relationship/model.py +++ b/backend/infrahub/core/relationship/model.py @@ -166,11 +166,11 @@ def get_branch_based_on_support_type(self) -> Branch: return registry.get_global_branch() return self.branch - async def _process_data(self, data: dict | RelationshipPeerData | str) -> None: + def _process_data(self, data: dict | RelationshipPeerData | str) -> None: self.data = data if isinstance(data, RelationshipPeerData): - await self.set_peer(value=str(data.peer_id)) + self.set_peer(value=str(data.peer_id)) if not self.id and data.rel_node_id: self.id = data.rel_node_id @@ -187,7 +187,7 @@ async def _process_data(self, data: dict | RelationshipPeerData | str) -> None: elif isinstance(data, dict): for key, value in data.items(): if key in ["peer", "id"]: - await self.set_peer(value=data.get(key, None)) + self.set_peer(value=data.get(key, None)) elif key == "hfid" and self.peer_id is None: self.peer_hfid = value elif key.startswith(PREFIX_PROPERTY) and key.replace(PREFIX_PROPERTY, "") in self._flag_properties: @@ -198,7 +198,7 @@ async def _process_data(self, data: dict | RelationshipPeerData | str) -> None: self.from_pool = value else: - await self.set_peer(value=data) + self.set_peer(value=data) async def new( self, @@ -206,11 +206,11 @@ async def new( data: dict | RelationshipPeerData | Any = None, **kwargs: Any, # noqa: ARG002 ) -> Relationship: - await self._process_data(data=data) + self._process_data(data=data) return self - async def load( + def load( self, db: InfrahubDatabase, # noqa: ARG002 id: UUID | None = None, @@ -223,7 +223,7 @@ async def load( self.id = id or self.id self.db_id = db_id or self.db_id - await self._process_data(data=data) + self._process_data(data=data) if updated_at and hash(self) != hash_before: self.updated_at = Timestamp(updated_at) @@ -252,7 +252,7 @@ async def get_node(self, db: InfrahubDatabase) -> Node: self._node_id = self._node.id return node - async def set_peer(self, value: str | Node) -> None: + def set_peer(self, value: str | Node) -> None: if isinstance(value, str): self.peer_id = value else: @@ -433,7 +433,7 @@ async def resolve(self, db: InfrahubDatabase, at: Timestamp | None = None) -> No db=db, id=self.peer_id, branch=self.branch, kind=self.schema.peer, fields={"display_label": None} ) if peer: - await self.set_peer(value=peer) + self.set_peer(value=peer) if not self.peer_id and self.peer_hfid: peer_schema = db.schema.get(name=self.schema.peer, branch=self.branch) @@ -450,7 +450,7 @@ async def resolve(self, db: InfrahubDatabase, at: Timestamp | None = None) -> No fields={"display_label": None}, raise_on_error=True, ) - await self.set_peer(value=peer) + self.set_peer(value=peer) if not self.peer_id and self.from_pool and "id" in self.from_pool: pool_id = str(self.from_pool.get("id")) @@ -473,7 +473,7 @@ async def resolve(self, db: InfrahubDatabase, at: Timestamp | None = None) -> No data_from_pool["identifier"] = f"hfid={hfid_str} rel={self.name}" assigned_peer: Node = await pool.get_resource(db=db, branch=self.branch, at=at, **data_from_pool) # type: ignore[attr-defined] - await self.set_peer(value=assigned_peer) + self.set_peer(value=assigned_peer) self.set_source(value=pool.id) async def save(self, db: InfrahubDatabase, at: Timestamp | None = None) -> Self: @@ -962,7 +962,7 @@ async def _fetch_relationships( for peer_id in details.peer_ids_present_database_only: self._relationships.append( - await Relationship( + Relationship( schema=self.schema, branch=self.branch, at=at or self.at, @@ -1050,7 +1050,7 @@ async def update(self, data: list[str | Node] | dict[str, Any] | str | Node | No if isinstance(item, dict) and item.get("id", None) in previous_relationships: rel = previous_relationships[item["id"]] hash_before = hash(rel) - await rel.load(data=item, db=db) + rel.load(data=item, db=db) if hash(rel) != hash_before: changed = True self._relationships.append(rel) diff --git a/backend/infrahub/graphql/mutations/relationship.py b/backend/infrahub/graphql/mutations/relationship.py index b5dca2496d..e89036a267 100644 --- a/backend/infrahub/graphql/mutations/relationship.py +++ b/backend/infrahub/graphql/mutations/relationship.py @@ -233,7 +233,7 @@ async def mutate( # we should use RelationshipDataDeleteQuery to delete the relationship # it would be more query efficient rel = Relationship(schema=rel_schema, branch=graphql_context.branch, node=source) - await rel.load(db=db, data=existing_peers[node_data.get("id")]) + rel.load(db=db, data=existing_peers[node_data.get("id")]) if group_event_type != GroupUpdateType.NONE: peers.append(EventNode(id=rel.get_peer_id(), kind=nodes[rel.get_peer_id()].get_kind())) node_changelog.delete_relationship(relationship=rel) diff --git a/backend/tests/unit/core/migrations/graph/test_003.py b/backend/tests/unit/core/migrations/graph/test_003.py index ae5358a793..202deb0332 100644 --- a/backend/tests/unit/core/migrations/graph/test_003.py +++ b/backend/tests/unit/core/migrations/graph/test_003.py @@ -2,22 +2,21 @@ from infrahub.core.migrations.graph.m003_relationship_parent_optional import Migration003, Migration003Query01 from infrahub.core.node import Node -from infrahub.core.schema import SchemaRoot, internal_schema from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.utils import count_relationships from infrahub.database import InfrahubDatabase @pytest.fixture -async def migration_003_data(db: InfrahubDatabase, reset_registry, default_branch, delete_all_nodes_in_db): - # # load the internal schema from - schema = SchemaRoot(**internal_schema) - schema_branch = SchemaBranch(cache={}, name="default_branch") - schema_branch.load_schema(schema=schema) - schema_branch.process() - - node_schema = schema_branch.get(name="SchemaNode") - rel_schema = schema_branch.get(name="SchemaRelationship") +async def migration_003_data( + db: InfrahubDatabase, + reset_registry, + default_branch, + delete_all_nodes_in_db, + register_core_models_schema: SchemaBranch, +): + node_schema = register_core_models_schema.get(name="SchemaNode") + rel_schema = register_core_models_schema.get(name="SchemaRelationship") node1 = await Node.init(db=db, schema=node_schema) await node1.new(db=db, name="Node", namespace="Test") diff --git a/backend/tests/unit/core/migrations/graph/test_012.py b/backend/tests/unit/core/migrations/graph/test_012.py index f7d41d91df..ca7cad90e0 100644 --- a/backend/tests/unit/core/migrations/graph/test_012.py +++ b/backend/tests/unit/core/migrations/graph/test_012.py @@ -10,8 +10,7 @@ Migration012RenameTypeAttributeSchema, ) from infrahub.core.node import Node -from infrahub.core.schema import AttributeSchema, NodeSchema, RelationshipSchema, SchemaRoot, internal_schema -from infrahub.core.schema.schema_branch import SchemaBranch +from infrahub.core.schema import AttributeSchema, NodeSchema, RelationshipSchema from infrahub.core.utils import count_nodes, count_relationships from infrahub.database import InfrahubDatabase @@ -134,13 +133,9 @@ @pytest.fixture -async def migration_012_data(db: InfrahubDatabase, reset_registry, default_branch, delete_all_nodes_in_db): - # # load the internal schema from - schema = SchemaRoot(**internal_schema) - schema_branch = SchemaBranch(cache={}, name="default_branch") - schema_branch.load_schema(schema=schema) - schema_branch.process() - +async def migration_012_data( + db: InfrahubDatabase, reset_registry, default_branch, delete_all_nodes_in_db, register_core_models_schema +): user1 = await Node.init(db=db, schema=ACCOUNT_SCHEMA) await user1.new(db=db, name="User1", type="User") await user1.save(db=db) @@ -163,12 +158,9 @@ async def migration_012_data(db: InfrahubDatabase, reset_registry, default_branc @pytest.fixture -async def migration_012_schema(db: InfrahubDatabase, reset_registry, default_branch, delete_all_nodes_in_db): - schema = SchemaRoot(**internal_schema) - schema_branch = SchemaBranch(cache={}, name="default_branch") - schema_branch.load_schema(schema=schema) - schema_branch.process() - +async def migration_012_schema( + db: InfrahubDatabase, reset_registry, default_branch, delete_all_nodes_in_db, register_core_models_schema +): node1 = await Node.init(db=db, schema=NODE_SCHEMA) await node1.new( db=db, name="Account", namespace="Core", inherit_from=[InfrahubKind.LINEAGEOWNER, InfrahubKind.LINEAGESOURCE] diff --git a/backend/tests/unit/core/migrations/graph/test_013.py b/backend/tests/unit/core/migrations/graph/test_013.py index 31e39c38c7..c41534de75 100644 --- a/backend/tests/unit/core/migrations/graph/test_013.py +++ b/backend/tests/unit/core/migrations/graph/test_013.py @@ -11,8 +11,7 @@ Migration013DeleteUsernamePasswordGenericSchema, ) from infrahub.core.node import Node -from infrahub.core.schema import AttributeSchema, NodeSchema, RelationshipSchema, SchemaRoot, internal_schema -from infrahub.core.schema.schema_branch import SchemaBranch +from infrahub.core.schema import AttributeSchema, NodeSchema, RelationshipSchema from infrahub.core.utils import count_nodes, count_relationships from infrahub.database import InfrahubDatabase @@ -141,13 +140,9 @@ @pytest.fixture -async def migration_013_data(db: InfrahubDatabase, reset_registry, default_branch, delete_all_nodes_in_db): - # load the internal schema from - schema = SchemaRoot(**internal_schema) - schema_branch = SchemaBranch(cache={}, name="default_branch") - schema_branch.load_schema(schema=schema) - schema_branch.process() - +async def migration_013_data( + db: InfrahubDatabase, reset_registry, default_branch, delete_all_nodes_in_db, register_core_models_schema +): repo1 = await Node.init(db=db, schema=GIT_SCHEMA) await repo1.new(db=db, name="repo1 initial", username="user1 initial", password="pwd1 initial") await repo1.save(db=db) @@ -168,12 +163,9 @@ async def migration_013_data(db: InfrahubDatabase, reset_registry, default_branc @pytest.fixture -async def migration_013_schema(db: InfrahubDatabase, reset_registry, default_branch, delete_all_nodes_in_db): - schema = SchemaRoot(**internal_schema) - schema_branch = SchemaBranch(cache={}, name="default_branch") - schema_branch.load_schema(schema=schema) - schema_branch.process() - +async def migration_013_schema( + db: InfrahubDatabase, reset_registry, default_branch, delete_all_nodes_in_db, register_core_models_schema +): node1 = await Node.init(db=db, schema=NODE_SCHEMA) await node1.new(db=db, name="GenericRepository", namespace="Core") await node1.save(db=db) diff --git a/backend/tests/unit/core/test_node.py b/backend/tests/unit/core/test_node.py index 8f9b36f451..02e16ece02 100644 --- a/backend/tests/unit/core/test_node.py +++ b/backend/tests/unit/core/test_node.py @@ -638,7 +638,7 @@ async def test_node_create_with_single_relationship(db: InfrahubDatabase, defaul assert c1.nbr_seats.value == 4 assert c1.is_electric.value is True c1_owner = await c1.owner.get_peer(db=db) - assert c1_owner == p1 + assert c1_owner.id == p1.id paths = await get_paths_between_nodes( db=db, source_id=c1.db_id, destination_id=p1.db_id, max_length=2, relationships=["IS_RELATED"] diff --git a/backend/tests/unit/core/test_relationship.py b/backend/tests/unit/core/test_relationship.py index 0613a696ca..4882f9a9c8 100644 --- a/backend/tests/unit/core/test_relationship.py +++ b/backend/tests/unit/core/test_relationship.py @@ -101,7 +101,7 @@ async def test_relationship_load_existing( assert peers[0].properties["is_protected"].value is True - await rel.load(db=db, data=peers[0]) + rel.load(db=db, data=peers[0]) assert rel.id == peers[0].rel_node_id assert rel.db_id == peers[0].rel_node_db_id @@ -115,7 +115,7 @@ async def test_relationship_peer(db: InfrahubDatabase, tag_blue_main: Node, pers rel_schema = person_schema.get_relationship("tags") rel = Relationship(schema=rel_schema, branch=branch, node=person_jack_main) - await rel.set_peer(value=tag_blue_main) + rel.set_peer(value=tag_blue_main) assert rel.schema == rel_schema assert rel.name == rel_schema.name @@ -131,7 +131,7 @@ async def test_relationship_save(db: InfrahubDatabase, tag_blue_main: Node, pers rel_schema = person_schema.get_relationship("tags") rel = Relationship(schema=rel_schema, branch=branch, node=person_jack_main) - await rel.set_peer(value=tag_blue_main) + rel.set_peer(value=tag_blue_main) await rel.save(db=db) p11 = await NodeManager.get_one(id=person_jack_main.id, db=db, branch=branch) @@ -147,28 +147,28 @@ async def test_relationship_hash( rel_schema = person_schema.get_relationship("tags") rel = Relationship(schema=rel_schema, branch=branch, node=person_jack_main) - await rel.set_peer(value=tag_blue_main) + rel.set_peer(value=tag_blue_main) await rel.save(db=db) hash1 = hash(rel) # Update flag property back and forth and check that hash is the same - await rel.load(db=db, data={"_relation__is_protected": True}) + rel.load(db=db, data={"_relation__is_protected": True}) hash2 = hash(rel) - await rel.load(db=db, data={"_relation__is_protected": False}) + rel.load(db=db, data={"_relation__is_protected": False}) hash3 = hash(rel) assert hash1 == hash3 assert hash1 != hash2 # Update node property back and forth and check that hash is the same as well - await rel.load(db=db, data={"_relation__owner": first_account}) + rel.load(db=db, data={"_relation__owner": first_account}) hash4 = hash(rel) - await rel.load(db=db, data={"_relation__owner": None}) + rel.load(db=db, data={"_relation__owner": None}) hash5 = hash(rel) - await rel.load(db=db, data={"_relation__owner": first_account}) + rel.load(db=db, data={"_relation__owner": first_account}) hash6 = hash(rel) assert hash4 == hash6 diff --git a/backend/tests/unit/core/test_relationship_query.py b/backend/tests/unit/core/test_relationship_query.py index 05289bbfda..33c32167ad 100644 --- a/backend/tests/unit/core/test_relationship_query.py +++ b/backend/tests/unit/core/test_relationship_query.py @@ -317,7 +317,7 @@ async def test_query_RelationshipDeleteQuery( ) rel = Relationship(schema=rel_schema, branch=branch, node=person_jack_tags_main) - await rel.load(db=db, data=rel_data) + rel.load(db=db, data=rel_data) query = await RelationshipDeleteQuery.init( db=db, @@ -386,7 +386,7 @@ def get_active_path_and_rel(all_paths, previous_rel: str): ) rel = Relationship(schema=rel_schema, branch=branch, node=person_jack_tags_main) - await rel.load(db=db, data=rel_data) + rel.load(db=db, data=rel_data) query = await RelationshipDeleteQuery.init( db=db,