Skip to content

Commit 38458d2

Browse files
authored
IFC-727 calculate diff tree in proposed change pipeline (#4546)
* remove test that belongs in sdk * calculate diff in pc pipeline * update integration test * point to SDK commit of partner PR * fix unit test, generate docs * clean up NodeDiff imports following SDK changes * force full import for docs generation * update python_sdk to latest stable commit * try to catch EntityNotFoundError
1 parent c2eb7cb commit 38458d2

File tree

10 files changed

+18
-252
lines changed

10 files changed

+18
-252
lines changed

backend/infrahub/core/validators/determiner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from collections import defaultdict
22
from typing import Union
33

4-
from infrahub_sdk.client import NodeDiff
4+
from infrahub_sdk.diff import NodeDiff
55

66
from infrahub.core.constants import RelationshipKind, SchemaPathType
77
from infrahub.core.constants.schema import UpdateSupport

backend/infrahub/database/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,10 @@ async def wrapper(*args, **kwargs):
417417
for attempt in range(1, config.SETTINGS.database.retry_limit + 1):
418418
try:
419419
return await func(*args, **kwargs)
420-
except TransientError as exc:
420+
except (TransientError, ClientError) as exc:
421+
if isinstance(exc, ClientError):
422+
if exc.code != "Neo.ClientError.Statement.EntityNotFound":
423+
raise exc
421424
retry_time: float = random.randrange(100, 500) / 1000
422425
log.info(
423426
f"Retrying database transaction, attempt {attempt}/{config.SETTINGS.database.retry_limit}",

backend/infrahub/message_bus/operations/requests/proposed_change.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ async def pipeline(message: messages.RequestProposedChangePipeline, service: Inf
134134

135135
await _gather_repository_repository_diffs(repositories=repositories)
136136

137+
destination_branch = await registry.get_branch(db=service.database, branch=message.destination_branch)
138+
source_branch = await registry.get_branch(db=service.database, branch=message.source_branch)
139+
component_registry = get_component_registry()
140+
async with service.database.start_transaction() as dbt:
141+
diff_coordinator = await component_registry.get_component(DiffCoordinator, db=dbt, branch=source_branch)
142+
await diff_coordinator.update_branch_diff(base_branch=destination_branch, diff_branch=source_branch)
137143
diff_summary = await service.client.get_diff_summary(branch=message.source_branch)
138144
branch_diff = ProposedChangeBranchDiff(diff_summary=diff_summary, repositories=repositories)
139145
await _populate_subscribers(branch_diff=branch_diff, service=service, branch=message.source_branch)

backend/infrahub/message_bus/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import re
44
from enum import Enum
55

6-
from infrahub_sdk.client import NodeDiff # noqa: TCH002
6+
from infrahub_sdk.diff import NodeDiff # noqa: TCH002
77
from pydantic import BaseModel, Field
88

99
from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus

backend/tests/integration/message_bus/operations/request/test_proposed_change.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ async def test_run_pipeline_validate_requested_jobs(
136136
services.service._client = client
137137
services.service.log = fake_log
138138
services.service.message_bus = bus_pre_data_changes
139+
services.service._database = db
139140
services.prepare(service=services.service)
140141
await pipeline(message=message, service=services.service)
141142

backend/tests/unit/core/constraint_validators/test_determiner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pytest
2-
from infrahub_sdk.client import NodeDiff
2+
from infrahub_sdk.diff import NodeDiff
33

44
from infrahub.core import registry
55
from infrahub.core.branch import Branch

backend/tests/unit/graphql/test_graphql_query.py

Lines changed: 0 additions & 246 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from infrahub.core import registry
99
from infrahub.core.branch import Branch
1010
from infrahub.core.constants import BranchSupportType, InfrahubKind
11-
from infrahub.core.initialization import create_branch
1211
from infrahub.core.manager import NodeManager
1312
from infrahub.core.node import Node
1413
from infrahub.core.schema import NodeSchema
@@ -687,251 +686,6 @@ async def test_display_label_nested_query(
687686
assert DeepDiff(result.data["TestPerson"]["edges"][0]["node"], expected_result, ignore_order=True).to_dict() == {}
688687

689688

690-
def _check_diff_for_branch_and_id(all_dicts: list[dict], branch_name: str, id: str, things_to_check: dict) -> dict:
691-
this_dict = None
692-
for one_dict in all_dicts:
693-
if one_dict["branch"] == branch_name and one_dict["id"] == id:
694-
this_dict = one_dict
695-
break
696-
if not this_dict:
697-
raise ValueError(f"No diff for branch={branch_name} and id={id}")
698-
for key, value in things_to_check.items():
699-
assert this_dict.get(key) == value
700-
return this_dict
701-
702-
703-
async def test_query_diffsummary(db: InfrahubDatabase, default_branch: Branch, car_person_schema: SchemaBranch):
704-
car = registry.schema.get(name="TestCar")
705-
person = registry.schema.get(name="TestPerson")
706-
707-
p1_main = await Node.init(db=db, schema=person)
708-
await p1_main.new(db=db, name="John", height=180)
709-
await p1_main.save(db=db)
710-
p2_main = await Node.init(db=db, schema=person)
711-
await p2_main.new(db=db, name="Jane", height=170)
712-
await p2_main.save(db=db)
713-
714-
c1_main = await Node.init(db=db, schema=car)
715-
await c1_main.new(db=db, name="volt", nbr_seats=4, is_electric=True, owner=p1_main)
716-
await c1_main.save(db=db)
717-
c2_main = await Node.init(db=db, schema=car)
718-
await c2_main.new(db=db, name="bolt", nbr_seats=4, is_electric=True, owner=p1_main)
719-
await c2_main.save(db=db)
720-
c3_main = await Node.init(db=db, schema=car)
721-
await c3_main.new(db=db, name="nolt", nbr_seats=4, is_electric=True, owner=p2_main)
722-
await c3_main.save(db=db)
723-
724-
branch2 = await create_branch(branch_name="branch2", db=db)
725-
await c1_main.delete(db=db)
726-
p1_branch2 = await NodeManager.get_one_by_id_or_default_filter(
727-
id=p1_main.id, db=db, kind="TestPerson", branch=branch2
728-
)
729-
p1_branch2.name.value = "Jonathan"
730-
await p1_branch2.save(db=db)
731-
p2_main.name.value = "Jeanette"
732-
await p2_main.save(db=db)
733-
c2_main.name.value = "bolting"
734-
await c2_main.save(db=db)
735-
c3_branch2 = await NodeManager.get_one_by_id_or_default_filter(id=c3_main.id, db=db, kind="TestCar", branch=branch2)
736-
await c3_branch2.owner.update(data=p1_branch2.id, db=db)
737-
await c3_branch2.save(db=db)
738-
739-
query = """
740-
query {
741-
DiffSummary {
742-
branch
743-
id
744-
kind
745-
action
746-
display_label
747-
elements {
748-
element_type
749-
name
750-
action
751-
summary {
752-
added
753-
updated
754-
removed
755-
}
756-
... on DiffSummaryElementRelationshipMany {
757-
peers {
758-
action
759-
summary {
760-
added
761-
updated
762-
removed
763-
}
764-
}
765-
}
766-
}
767-
}
768-
}
769-
"""
770-
gql_params = prepare_graphql_params(db=db, include_mutation=False, include_subscription=False, branch=branch2)
771-
result = await graphql(
772-
schema=gql_params.schema,
773-
source=query,
774-
context_value=gql_params.context,
775-
root_value=None,
776-
variable_values={},
777-
)
778-
779-
assert result.errors is None
780-
assert result.data
781-
diff_summary = result.data["DiffSummary"]
782-
783-
assert len(diff_summary) == 5
784-
785-
_check_diff_for_branch_and_id(
786-
all_dicts=diff_summary,
787-
branch_name="main",
788-
id=c1_main.id,
789-
things_to_check={
790-
"branch": "main",
791-
"id": c1_main.id,
792-
"kind": "TestCar",
793-
"action": "REMOVED",
794-
"display_label": "",
795-
},
796-
)
797-
_check_diff_for_branch_and_id(
798-
all_dicts=diff_summary,
799-
branch_name="main",
800-
id=c2_main.id,
801-
things_to_check={
802-
"branch": "main",
803-
"id": c2_main.id,
804-
"kind": "TestCar",
805-
"action": "UPDATED",
806-
"display_label": "bolting #444444",
807-
"elements": [
808-
{
809-
"element_type": "ATTRIBUTE",
810-
"name": "name",
811-
"action": "UPDATED",
812-
"summary": {"added": 0, "updated": 1, "removed": 0},
813-
}
814-
],
815-
},
816-
)
817-
c3_branch2_diff = _check_diff_for_branch_and_id(
818-
all_dicts=diff_summary,
819-
branch_name="branch2",
820-
id=c3_branch2.id,
821-
things_to_check={
822-
"branch": "branch2",
823-
"id": c3_branch2.id,
824-
"kind": "TestCar",
825-
"action": "UPDATED",
826-
"display_label": "nolt #444444",
827-
},
828-
)
829-
c3_branch2_diff_elements = c3_branch2_diff["elements"]
830-
assert len(c3_branch2_diff_elements) == 1
831-
assert c3_branch2_diff_elements[0]["element_type"] == "RELATIONSHIP_ONE"
832-
assert c3_branch2_diff_elements[0]["name"] == "owner"
833-
assert c3_branch2_diff_elements[0]["action"] == "UPDATED"
834-
p2_main_diff = _check_diff_for_branch_and_id(
835-
all_dicts=diff_summary,
836-
branch_name="main",
837-
id=p2_main.id,
838-
things_to_check={
839-
"branch": "main",
840-
"id": p2_main.id,
841-
"kind": "TestPerson",
842-
"action": "UPDATED",
843-
"display_label": "Jeanette",
844-
},
845-
)
846-
p2_main_diff_elements = p2_main_diff["elements"]
847-
assert len(p2_main_diff_elements) == 1
848-
assert p2_main_diff_elements[0]["element_type"] == "ATTRIBUTE"
849-
assert p2_main_diff_elements[0]["name"] == "name"
850-
assert p2_main_diff_elements[0]["action"] == "UPDATED"
851-
assert p2_main_diff_elements[0]["summary"] == {"added": 0, "updated": 1, "removed": 0}
852-
p1_branch2_diff = _check_diff_for_branch_and_id(
853-
all_dicts=diff_summary,
854-
branch_name="branch2",
855-
id=p1_branch2.id,
856-
things_to_check={
857-
"branch": "branch2",
858-
"id": p1_branch2.id,
859-
"kind": "TestPerson",
860-
"action": "UPDATED",
861-
"display_label": "Jonathan",
862-
},
863-
)
864-
p1_branch2_diff_elements = p1_branch2_diff["elements"]
865-
assert len(p1_branch2_diff_elements) == 2
866-
p1_branch2_diff_elements_map = {elem["name"]: elem for elem in p1_branch2_diff_elements}
867-
assert {"cars", "name"} == set(p1_branch2_diff_elements_map.keys())
868-
name_element = p1_branch2_diff_elements_map["name"]
869-
assert name_element["name"] == "name"
870-
assert name_element["element_type"] == "ATTRIBUTE"
871-
assert name_element["action"] == "UPDATED"
872-
cars_element = p1_branch2_diff_elements_map["cars"]
873-
assert cars_element["name"] == "cars"
874-
assert cars_element["element_type"] == "RELATIONSHIP_MANY"
875-
assert cars_element["action"] == "ADDED"
876-
assert len(cars_element["peers"]) == 1
877-
assert cars_element["peers"][0]["action"] == "ADDED"
878-
879-
880-
async def test_diffsummary_on_default_branch(
881-
db: InfrahubDatabase, default_branch: Branch, car_person_schema: SchemaBranch
882-
):
883-
person = registry.schema.get(name="TestPerson")
884-
885-
before_create = Timestamp()
886-
p1 = await Node.init(db=db, schema=person)
887-
await p1.new(db=db, name="John", height=180)
888-
await p1.save(db=db)
889-
p2 = await Node.init(db=db, schema=person)
890-
await p2.new(db=db, name="Jane", height=170)
891-
await p2.save(db=db)
892-
893-
query = """
894-
query DiffSummaries($time_from: String) {
895-
DiffSummary(time_from: $time_from) {
896-
branch
897-
id
898-
kind
899-
action
900-
}
901-
}
902-
"""
903-
gql_params = prepare_graphql_params(
904-
db=db, include_mutation=False, include_subscription=False, branch=default_branch
905-
)
906-
result = await graphql(
907-
schema=gql_params.schema,
908-
source=query,
909-
context_value=gql_params.context,
910-
root_value=None,
911-
variable_values={},
912-
)
913-
assert result.errors
914-
assert len(result.errors) == 1
915-
assert result.errors[0].message == "time_from is required on default branch"
916-
917-
gql_params = prepare_graphql_params(
918-
db=db, include_mutation=False, include_subscription=False, branch=default_branch
919-
)
920-
result = await graphql(
921-
schema=gql_params.schema,
922-
source=query,
923-
context_value=gql_params.context,
924-
root_value=None,
925-
variable_values={"time_from": before_create.to_string()},
926-
)
927-
assert result.errors is None
928-
assert result.data
929-
summaries = result.data["DiffSummary"]
930-
assert len(summaries) == 2
931-
assert {"branch": default_branch.name, "kind": "TestPerson", "id": p1.get_id(), "action": "ADDED"} in summaries
932-
assert {"branch": default_branch.name, "kind": "TestPerson", "id": p2.get_id(), "action": "ADDED"} in summaries
933-
934-
935689
async def test_query_typename(db: InfrahubDatabase, default_branch: Branch, car_person_schema: SchemaBranch):
936690
car = registry.schema.get(name="TestCar")
937691
person = registry.schema.get(name="TestPerson")

backend/tests/unit/message_bus/operations/requests/test_proposed_change.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ async def test_schema_integrity(
288288
branch2_schema.set(name="TestPerson", schema=person_schema)
289289

290290
# Ignore creation of Task Report response
291-
httpx_mock.add_response(method="POST", url="http://mock/graphql", json={"data": {}})
291+
httpx_mock.add_response(method="POST", url="http://mock/graphql/main", json={"data": {}})
292292
await proposed_change.schema_integrity(message=schema_integrity_01, service=service_all)
293293

294294
checks = await registry.manager.query(db=db, schema=InfrahubKind.SCHEMACHECK)

docs/docs/infrahubctl/infrahubctl-protocols.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ $ infrahubctl protocols [OPTIONS]
1010

1111
**Options**:
1212

13+
* `--schemas PATH`: List of schemas or directory to load.
1314
* `--branch TEXT`: Branch of schema to export Python protocols for.
15+
* `--sync / --no-sync`: Generate for sync or async. [default: no-sync]
1416
* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml]
1517
* `--out TEXT`: Path to a file to save the result. [default: schema_protocols.py]
1618
* `--install-completion`: Install completion for the current shell.

0 commit comments

Comments
 (0)