Skip to content

Commit 2553ba2

Browse files
authored
Merge pull request #5763 from opsmill/pog-mutation-events-only-on-updates-IFC-1247
Only send node mutation events when there has been a change
2 parents 927ff90 + 6a540ef commit 2553ba2

File tree

3 files changed

+85
-1
lines changed

3 files changed

+85
-1
lines changed

backend/infrahub/core/changelog/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ def updated_fields(self) -> list[str]:
228228
"""Return a list of update fields i.e. attributes and relationships"""
229229
return list(self.relationships.keys()) + list(self.attributes.keys())
230230

231+
@property
232+
def has_changes(self) -> bool:
233+
return len(self.updated_fields) > 0
234+
231235
@property
232236
def root_node_id(self) -> str:
233237
"""Return the top level node_id"""

backend/infrahub/graphql/mutations/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ async def mutate(cls, root: dict, info: GraphQLResolveInfo, data: InputObjectTyp
9393
# Reset the time of the query to guarantee that all resolvers executed after this point will account for the changes
9494
graphql_context.at = Timestamp()
9595

96-
if config.SETTINGS.broker.enable and graphql_context.background:
96+
if config.SETTINGS.broker.enable and graphql_context.background and obj.node_changelog.has_changes:
9797
log_data = get_log_data()
9898
request_id = log_data.get("request_id", "")
9999

backend/tests/unit/graphql/test_mutation_upsert.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
from uuid import uuid4
22

3+
from infrahub.auth import AccountSession
34
from infrahub.core.branch import Branch
45
from infrahub.core.manager import NodeManager
56
from infrahub.core.node import Node
67
from infrahub.core.registry import registry
78
from infrahub.core.schema import SchemaRoot
89
from infrahub.database import InfrahubDatabase
10+
from infrahub.events.node_action import NodeMutatedEvent
911
from infrahub.graphql.initialization import prepare_graphql_params
12+
from infrahub.services import InfrahubServices
13+
from tests.adapters.event import MemoryInfrahubEvent
1014
from tests.constants import TestKind
1115
from tests.helpers.graphql import graphql
1216
from tests.helpers.schema import TICKET
@@ -33,6 +37,7 @@ async def test_upsert_existing_simple_object_by_id(db: InfrahubDatabase, person_
3337
)
3438

3539
assert result.errors is None
40+
assert result.data
3641
assert result.data["TestPersonUpsert"]["ok"] is True
3742

3843
obj1 = await NodeManager.get_one(db=db, id=person_john_main.id, branch=branch)
@@ -60,13 +65,81 @@ async def test_upsert_existing_simple_object_by_default_filter(
6065
)
6166

6267
assert result.errors is None
68+
assert result.data
6369
assert result.data["TestPersonUpsert"]["ok"] is True
6470

6571
obj1 = await NodeManager.get_one(db=db, id=person_john_main.id, branch=branch)
6672
assert obj1.name.value == "John"
6773
assert obj1.height.value == 138
6874

6975

76+
async def test_upsert_event_on_no_change(
77+
db: InfrahubDatabase,
78+
car_person_schema: Node,
79+
branch: Branch,
80+
enable_broker_config: None,
81+
session_first_account: AccountSession,
82+
) -> None:
83+
query = """
84+
mutation {
85+
TestPersonUpsert(data: {name: { value: "Howard"}, height: {value: 174}}) {
86+
ok
87+
object {
88+
id
89+
}
90+
}
91+
}
92+
"""
93+
memory_event = MemoryInfrahubEvent()
94+
service = await InfrahubServices.new(event=memory_event)
95+
gql_params = await prepare_graphql_params(
96+
db=db, include_subscription=False, branch=branch, service=service, account_session=session_first_account
97+
)
98+
result = await graphql(
99+
schema=gql_params.schema,
100+
source=query,
101+
context_value=gql_params.context,
102+
root_value=None,
103+
variable_values={},
104+
)
105+
106+
assert result.errors is None
107+
assert result.data
108+
assert result.data["TestPersonUpsert"]["ok"] is True
109+
howard_id = result.data["TestPersonUpsert"]["object"]["id"]
110+
111+
obj1 = await NodeManager.get_one(db=db, id=howard_id, branch=branch)
112+
assert obj1.name.value == "Howard"
113+
assert obj1.height.value == 174
114+
115+
assert gql_params.context.background
116+
await gql_params.context.background()
117+
assert len(memory_event.events) == 1
118+
event = memory_event.events[0]
119+
assert isinstance(event, NodeMutatedEvent)
120+
assert sorted(event.data.attributes.keys()) == ["height", "name"]
121+
122+
memory_event = MemoryInfrahubEvent()
123+
service = await InfrahubServices.new(event=memory_event)
124+
gql_params = await prepare_graphql_params(
125+
db=db, include_subscription=False, branch=branch, service=service, account_session=session_first_account
126+
)
127+
result_second_time = await graphql(
128+
schema=gql_params.schema,
129+
source=query,
130+
context_value=gql_params.context,
131+
root_value=None,
132+
variable_values={},
133+
)
134+
assert result_second_time.errors is None
135+
assert result_second_time.data
136+
assert result_second_time.data["TestPersonUpsert"]["ok"] is True
137+
138+
assert gql_params.context.background
139+
await gql_params.context.background()
140+
assert len(memory_event.events) == 0
141+
142+
70143
async def test_upsert_create_simple_object_no_id(db: InfrahubDatabase, person_john_main, branch: Branch):
71144
query = """
72145
mutation {
@@ -89,6 +162,7 @@ async def test_upsert_create_simple_object_no_id(db: InfrahubDatabase, person_jo
89162
)
90163

91164
assert result.errors is None
165+
assert result.data
92166
assert result.data["TestPersonUpsert"]["ok"] is True
93167

94168
person_id = result.data["TestPersonUpsert"]["object"]["id"]
@@ -120,6 +194,7 @@ async def test_upsert_create_simple_object_with_id(db: InfrahubDatabase, person_
120194
)
121195

122196
assert result.errors is None
197+
assert result.data
123198
assert result.data["TestPersonUpsert"]["ok"] is True
124199

125200
person_id = result.data["TestPersonUpsert"]["object"]["id"]
@@ -151,6 +226,7 @@ async def test_cannot_upsert_new_object_without_required_fields(db: InfrahubData
151226
)
152227

153228
expected_error = "Field 'TestPersonUpsertInput.name' of required type 'TextAttributeUpdate!' was not provided."
229+
assert result.errors
154230
assert any(expected_error in error.message for error in result.errors)
155231

156232
assert await NodeManager.get_one(db=db, id=fresh_id, branch=branch) is None
@@ -179,6 +255,7 @@ async def test_id_for_other_schema_raises_error(
179255
)
180256

181257
expected_error = f"Node with id {car_accord_main.id} exists, but it is a TestCar, not TestPerson"
258+
assert result.errors
182259
assert any(expected_error in error.message for error in result.errors)
183260

184261

@@ -205,6 +282,7 @@ async def test_update_by_id_to_nonunique_value_raises_error(
205282
)
206283

207284
expected_error = "Violates uniqueness constraint 'name' at name"
285+
assert result.errors
208286
assert any(expected_error in error.message for error in result.errors)
209287

210288

@@ -251,6 +329,7 @@ async def test_with_hfid_existing(db: InfrahubDatabase, default_branch, animal_p
251329
variable_values={},
252330
)
253331
assert result.errors is None
332+
assert result.data
254333
assert result.data["TestDogUpsert"]["ok"] is True
255334
assert result.data["TestDogUpsert"]["object"] == {"color": {"value": "black"}, "id": dog1.id}
256335

@@ -305,6 +384,7 @@ async def test_with_hfid_new(db: InfrahubDatabase, default_branch, animal_person
305384
variable_values={},
306385
)
307386
assert result.errors is None
387+
assert result.data
308388
assert result.data["TestDogUpsert"]["ok"] is True
309389
new_id = result.data["TestDogUpsert"]["object"]["id"]
310390
assert result.data["TestDogUpsert"]["object"] == {

0 commit comments

Comments
 (0)