infrahub-server 1.6.3__py3-none-any.whl → 1.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. infrahub/actions/tasks.py +4 -2
  2. infrahub/api/exceptions.py +2 -2
  3. infrahub/api/schema.py +3 -1
  4. infrahub/artifacts/tasks.py +1 -0
  5. infrahub/auth.py +2 -2
  6. infrahub/cli/db.py +54 -28
  7. infrahub/computed_attribute/gather.py +3 -4
  8. infrahub/computed_attribute/tasks.py +23 -6
  9. infrahub/config.py +8 -0
  10. infrahub/constants/enums.py +12 -0
  11. infrahub/core/account.py +4 -4
  12. infrahub/core/attribute.py +106 -108
  13. infrahub/core/branch/models.py +44 -71
  14. infrahub/core/branch/tasks.py +5 -3
  15. infrahub/core/changelog/diff.py +1 -20
  16. infrahub/core/changelog/models.py +0 -7
  17. infrahub/core/constants/__init__.py +17 -0
  18. infrahub/core/constants/database.py +0 -1
  19. infrahub/core/constants/schema.py +0 -1
  20. infrahub/core/convert_object_type/repository_conversion.py +3 -4
  21. infrahub/core/diff/branch_differ.py +1 -1
  22. infrahub/core/diff/conflict_transferer.py +1 -1
  23. infrahub/core/diff/data_check_synchronizer.py +4 -3
  24. infrahub/core/diff/enricher/cardinality_one.py +2 -2
  25. infrahub/core/diff/enricher/hierarchy.py +1 -1
  26. infrahub/core/diff/enricher/labels.py +1 -1
  27. infrahub/core/diff/merger/merger.py +28 -2
  28. infrahub/core/diff/merger/serializer.py +3 -10
  29. infrahub/core/diff/model/diff.py +1 -1
  30. infrahub/core/diff/query/merge.py +376 -135
  31. infrahub/core/diff/repository/repository.py +3 -1
  32. infrahub/core/graph/__init__.py +1 -1
  33. infrahub/core/graph/constraints.py +3 -3
  34. infrahub/core/graph/schema.py +2 -12
  35. infrahub/core/ipam/reconciler.py +8 -6
  36. infrahub/core/ipam/utilization.py +8 -15
  37. infrahub/core/manager.py +133 -152
  38. infrahub/core/merge.py +1 -1
  39. infrahub/core/metadata/__init__.py +0 -0
  40. infrahub/core/metadata/interface.py +37 -0
  41. infrahub/core/metadata/model.py +31 -0
  42. infrahub/core/metadata/query/__init__.py +0 -0
  43. infrahub/core/metadata/query/node_metadata.py +301 -0
  44. infrahub/core/migrations/graph/__init__.py +4 -0
  45. infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -12
  46. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -12
  47. infrahub/core/migrations/graph/m017_add_core_profile.py +5 -2
  48. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -1
  49. infrahub/core/migrations/graph/m019_restore_rels_to_time.py +0 -10
  50. infrahub/core/migrations/graph/m020_duplicate_edges.py +0 -8
  51. infrahub/core/migrations/graph/m025_uniqueness_nulls.py +2 -1
  52. infrahub/core/migrations/graph/m026_0000_prefix_fix.py +2 -1
  53. infrahub/core/migrations/graph/m029_duplicates_cleanup.py +0 -1
  54. infrahub/core/migrations/graph/m031_check_number_attributes.py +2 -2
  55. infrahub/core/migrations/graph/m038_redo_0000_prefix_fix.py +2 -1
  56. infrahub/core/migrations/graph/m041_deleted_dup_edges.py +1 -1
  57. infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +53 -0
  58. infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +168 -0
  59. infrahub/core/migrations/query/__init__.py +2 -2
  60. infrahub/core/migrations/query/attribute_add.py +17 -6
  61. infrahub/core/migrations/query/attribute_remove.py +19 -5
  62. infrahub/core/migrations/query/attribute_rename.py +21 -5
  63. infrahub/core/migrations/query/node_duplicate.py +19 -4
  64. infrahub/core/migrations/query/schema_attribute_update.py +1 -1
  65. infrahub/core/migrations/schema/attribute_kind_update.py +21 -2
  66. infrahub/core/migrations/schema/attribute_name_update.py +1 -1
  67. infrahub/core/migrations/schema/attribute_supports_profile.py +5 -3
  68. infrahub/core/migrations/schema/models.py +3 -0
  69. infrahub/core/migrations/schema/node_attribute_add.py +5 -2
  70. infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
  71. infrahub/core/migrations/schema/node_kind_update.py +1 -1
  72. infrahub/core/migrations/schema/node_remove.py +24 -2
  73. infrahub/core/migrations/schema/tasks.py +4 -1
  74. infrahub/core/migrations/shared.py +13 -6
  75. infrahub/core/models.py +6 -6
  76. infrahub/core/node/__init__.py +157 -58
  77. infrahub/core/node/base.py +9 -5
  78. infrahub/core/node/create.py +7 -3
  79. infrahub/core/node/delete_validator.py +1 -1
  80. infrahub/core/node/standard.py +100 -14
  81. infrahub/core/order.py +30 -0
  82. infrahub/core/property.py +0 -1
  83. infrahub/core/protocols.py +1 -0
  84. infrahub/core/protocols_base.py +10 -2
  85. infrahub/core/query/__init__.py +5 -3
  86. infrahub/core/query/attribute.py +164 -49
  87. infrahub/core/query/branch.py +58 -70
  88. infrahub/core/query/delete.py +1 -1
  89. infrahub/core/query/diff.py +7 -7
  90. infrahub/core/query/ipam.py +104 -43
  91. infrahub/core/query/node.py +1072 -281
  92. infrahub/core/query/relationship.py +531 -325
  93. infrahub/core/query/resource_manager.py +107 -18
  94. infrahub/core/query/standard_node.py +25 -5
  95. infrahub/core/query/utils.py +2 -4
  96. infrahub/core/relationship/constraints/count.py +1 -1
  97. infrahub/core/relationship/constraints/peer_kind.py +1 -1
  98. infrahub/core/relationship/constraints/peer_parent.py +1 -1
  99. infrahub/core/relationship/constraints/peer_relatives.py +1 -1
  100. infrahub/core/relationship/constraints/profiles_kind.py +1 -1
  101. infrahub/core/relationship/constraints/profiles_removal.py +168 -0
  102. infrahub/core/relationship/model.py +293 -139
  103. infrahub/core/schema/attribute_schema.py +2 -2
  104. infrahub/core/schema/basenode_schema.py +3 -0
  105. infrahub/core/schema/definitions/core/__init__.py +8 -2
  106. infrahub/core/schema/definitions/core/account.py +10 -10
  107. infrahub/core/schema/definitions/core/artifact.py +14 -8
  108. infrahub/core/schema/definitions/core/check.py +10 -4
  109. infrahub/core/schema/definitions/core/generator.py +26 -6
  110. infrahub/core/schema/definitions/core/graphql_query.py +1 -1
  111. infrahub/core/schema/definitions/core/group.py +9 -2
  112. infrahub/core/schema/definitions/core/ipam.py +80 -10
  113. infrahub/core/schema/definitions/core/menu.py +41 -7
  114. infrahub/core/schema/definitions/core/permission.py +16 -2
  115. infrahub/core/schema/definitions/core/profile.py +16 -2
  116. infrahub/core/schema/definitions/core/propose_change.py +24 -4
  117. infrahub/core/schema/definitions/core/propose_change_comment.py +23 -11
  118. infrahub/core/schema/definitions/core/propose_change_validator.py +50 -21
  119. infrahub/core/schema/definitions/core/repository.py +10 -0
  120. infrahub/core/schema/definitions/core/resource_pool.py +8 -1
  121. infrahub/core/schema/definitions/core/template.py +19 -2
  122. infrahub/core/schema/definitions/core/transform.py +11 -5
  123. infrahub/core/schema/definitions/core/webhook.py +27 -9
  124. infrahub/core/schema/manager.py +63 -43
  125. infrahub/core/schema/relationship_schema.py +6 -2
  126. infrahub/core/schema/schema_branch.py +48 -10
  127. infrahub/core/task/task.py +4 -2
  128. infrahub/core/utils.py +3 -25
  129. infrahub/core/validators/aggregated_checker.py +1 -1
  130. infrahub/core/validators/attribute/choices.py +1 -1
  131. infrahub/core/validators/attribute/enum.py +1 -1
  132. infrahub/core/validators/attribute/kind.py +1 -1
  133. infrahub/core/validators/attribute/length.py +1 -1
  134. infrahub/core/validators/attribute/min_max.py +1 -1
  135. infrahub/core/validators/attribute/number_pool.py +1 -1
  136. infrahub/core/validators/attribute/optional.py +1 -1
  137. infrahub/core/validators/attribute/regex.py +1 -1
  138. infrahub/core/validators/determiner.py +3 -3
  139. infrahub/core/validators/node/attribute.py +1 -1
  140. infrahub/core/validators/node/relationship.py +1 -1
  141. infrahub/core/validators/relationship/peer.py +1 -1
  142. infrahub/database/__init__.py +4 -4
  143. infrahub/dependencies/builder/constraint/grouped/node_runner.py +2 -0
  144. infrahub/dependencies/builder/constraint/relationship_manager/profiles_removal.py +8 -0
  145. infrahub/dependencies/registry.py +2 -0
  146. infrahub/display_labels/tasks.py +12 -3
  147. infrahub/git/integrator.py +18 -18
  148. infrahub/git/tasks.py +1 -1
  149. infrahub/git/utils.py +1 -1
  150. infrahub/graphql/constants.py +3 -0
  151. infrahub/graphql/context.py +1 -1
  152. infrahub/graphql/field_extractor.py +1 -1
  153. infrahub/graphql/initialization.py +11 -0
  154. infrahub/graphql/loaders/account.py +134 -0
  155. infrahub/graphql/loaders/node.py +5 -12
  156. infrahub/graphql/loaders/peers.py +5 -7
  157. infrahub/graphql/manager.py +175 -21
  158. infrahub/graphql/metadata.py +91 -0
  159. infrahub/graphql/mutations/account.py +6 -6
  160. infrahub/graphql/mutations/attribute.py +0 -2
  161. infrahub/graphql/mutations/branch.py +9 -5
  162. infrahub/graphql/mutations/computed_attribute.py +1 -1
  163. infrahub/graphql/mutations/display_label.py +1 -1
  164. infrahub/graphql/mutations/hfid.py +1 -1
  165. infrahub/graphql/mutations/ipam.py +4 -6
  166. infrahub/graphql/mutations/main.py +9 -4
  167. infrahub/graphql/mutations/profile.py +16 -22
  168. infrahub/graphql/mutations/proposed_change.py +4 -4
  169. infrahub/graphql/mutations/relationship.py +40 -10
  170. infrahub/graphql/mutations/repository.py +14 -12
  171. infrahub/graphql/mutations/schema.py +2 -2
  172. infrahub/graphql/order.py +14 -0
  173. infrahub/graphql/queries/branch.py +62 -6
  174. infrahub/graphql/queries/resource_manager.py +25 -24
  175. infrahub/graphql/resolvers/account_metadata.py +84 -0
  176. infrahub/graphql/resolvers/ipam.py +6 -8
  177. infrahub/graphql/resolvers/many_relationship.py +77 -35
  178. infrahub/graphql/resolvers/resolver.py +59 -14
  179. infrahub/graphql/resolvers/single_relationship.py +87 -23
  180. infrahub/graphql/subscription/graphql_query.py +2 -0
  181. infrahub/graphql/types/__init__.py +0 -1
  182. infrahub/graphql/types/attribute.py +10 -5
  183. infrahub/graphql/types/branch.py +40 -53
  184. infrahub/graphql/types/enums.py +3 -0
  185. infrahub/graphql/types/metadata.py +28 -0
  186. infrahub/graphql/types/node.py +22 -2
  187. infrahub/graphql/types/relationship.py +10 -2
  188. infrahub/graphql/types/standard_node.py +12 -7
  189. infrahub/hfid/tasks.py +12 -3
  190. infrahub/lock.py +7 -0
  191. infrahub/menu/repository.py +1 -1
  192. infrahub/patch/queries/base.py +1 -1
  193. infrahub/pools/number.py +1 -8
  194. infrahub/profiles/gather.py +56 -0
  195. infrahub/profiles/mandatory_fields_checker.py +116 -0
  196. infrahub/profiles/models.py +66 -0
  197. infrahub/profiles/node_applier.py +154 -13
  198. infrahub/profiles/queries/get_profile_data.py +143 -31
  199. infrahub/profiles/tasks.py +79 -27
  200. infrahub/profiles/triggers.py +22 -0
  201. infrahub/proposed_change/action_checker.py +1 -1
  202. infrahub/proposed_change/tasks.py +4 -1
  203. infrahub/services/__init__.py +1 -1
  204. infrahub/services/adapters/cache/nats.py +1 -1
  205. infrahub/services/adapters/cache/redis.py +7 -0
  206. infrahub/tasks/artifact.py +1 -0
  207. infrahub/transformations/tasks.py +2 -2
  208. infrahub/trigger/catalogue.py +2 -0
  209. infrahub/trigger/models.py +1 -0
  210. infrahub/trigger/setup.py +3 -3
  211. infrahub/trigger/tasks.py +3 -0
  212. infrahub/validators/tasks.py +1 -0
  213. infrahub/webhook/gather.py +1 -1
  214. infrahub/webhook/models.py +1 -1
  215. infrahub/webhook/tasks.py +23 -7
  216. infrahub/workers/dependencies.py +9 -3
  217. infrahub/workers/infrahub_async.py +13 -4
  218. infrahub/workflows/catalogue.py +19 -0
  219. infrahub_sdk/analyzer.py +2 -2
  220. infrahub_sdk/branch.py +12 -39
  221. infrahub_sdk/checks.py +4 -4
  222. infrahub_sdk/client.py +36 -0
  223. infrahub_sdk/ctl/cli_commands.py +2 -1
  224. infrahub_sdk/ctl/graphql.py +15 -4
  225. infrahub_sdk/ctl/utils.py +2 -2
  226. infrahub_sdk/enums.py +6 -0
  227. infrahub_sdk/graphql/renderers.py +21 -0
  228. infrahub_sdk/graphql/utils.py +85 -0
  229. infrahub_sdk/node/attribute.py +12 -2
  230. infrahub_sdk/node/constants.py +12 -0
  231. infrahub_sdk/node/metadata.py +69 -0
  232. infrahub_sdk/node/node.py +65 -14
  233. infrahub_sdk/node/property.py +3 -0
  234. infrahub_sdk/node/related_node.py +37 -5
  235. infrahub_sdk/node/relationship.py +18 -1
  236. infrahub_sdk/operation.py +2 -2
  237. infrahub_sdk/schema/repository.py +1 -2
  238. infrahub_sdk/transforms.py +2 -2
  239. infrahub_sdk/types.py +18 -2
  240. {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/METADATA +17 -16
  241. {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/RECORD +249 -228
  242. infrahub_testcontainers/container.py +3 -3
  243. infrahub_testcontainers/docker-compose-cluster.test.yml +7 -7
  244. infrahub_testcontainers/docker-compose.test.yml +13 -5
  245. infrahub_testcontainers/models.py +3 -3
  246. infrahub_testcontainers/performance_test.py +1 -1
  247. infrahub/graphql/models.py +0 -6
  248. {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/WHEEL +0 -0
  249. {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/entry_points.txt +0 -0
  250. {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -116,13 +116,6 @@ class DiffChangelogCollector:
116
116
  value_current=self._convert_string_boolean_value(value=attr_property.new_value),
117
117
  value_previous=self._convert_string_boolean_value(value=attr_property.previous_value),
118
118
  )
119
- case DatabaseEdgeType.IS_VISIBLE:
120
- if _keep_branch_update(diff_property=attr_property):
121
- changelog_attribute.add_property(
122
- name="is_visible",
123
- value_current=self._convert_string_boolean_value(value=attr_property.new_value),
124
- value_previous=self._convert_string_boolean_value(value=attr_property.previous_value),
125
- )
126
119
  case DatabaseEdgeType.HAS_SOURCE:
127
120
  if _keep_branch_update(diff_property=attr_property):
128
121
  changelog_attribute.add_property(
@@ -176,12 +169,6 @@ class DiffChangelogCollector:
176
169
  value_current=self._convert_string_boolean_value(value=rel_prop.new_value),
177
170
  value_previous=self._convert_string_boolean_value(value=rel_prop.previous_value),
178
171
  )
179
- case DatabaseEdgeType.IS_VISIBLE:
180
- changelog_rel.add_property(
181
- name="is_visible",
182
- value_current=self._convert_string_boolean_value(value=rel_prop.new_value),
183
- value_previous=self._convert_string_boolean_value(value=rel_prop.previous_value),
184
- )
185
172
  case DatabaseEdgeType.HAS_OWNER:
186
173
  changelog_rel.add_property(
187
174
  name="owner",
@@ -198,7 +185,7 @@ class DiffChangelogCollector:
198
185
  node.add_relationship(relationship_changelog=changelog_rel)
199
186
 
200
187
  def _convert_string_boolean_value(self, value: str | None) -> bool | None:
201
- """Convert string based boolean for is_protected and is_visible."""
188
+ """Convert string based boolean for is_protected."""
202
189
  if value is not None:
203
190
  return str_to_bool(value)
204
191
 
@@ -218,12 +205,6 @@ class DiffChangelogCollector:
218
205
  )
219
206
  for peer_prop in peer.properties:
220
207
  match peer_prop.property_type:
221
- case DatabaseEdgeType.IS_VISIBLE:
222
- peer_log.add_property(
223
- name="is_visible",
224
- value_current=self._convert_string_boolean_value(value=peer_prop.new_value),
225
- value_previous=self._convert_string_boolean_value(value=peer_prop.previous_value),
226
- )
227
208
  case DatabaseEdgeType.IS_PROTECTED:
228
209
  peer_log.add_property(
229
210
  name="is_protected",
@@ -186,9 +186,6 @@ class RelationshipCardinalityManyChangelog(BaseModel):
186
186
  properties["is_protected"] = PropertyChangelog(
187
187
  name="is_protected", value=relationship.is_protected, value_previous=None
188
188
  )
189
- properties["is_visible"] = PropertyChangelog(
190
- name="is_visible", value=relationship.is_protected, value_previous=None
191
- )
192
189
  if owner := getattr(relationship, "owner_id", None):
193
190
  properties["owner"] = PropertyChangelog(name="owner", value=owner, value_previous=None)
194
191
  if source := getattr(relationship, "source_id", None):
@@ -280,9 +277,6 @@ class NodeChangelog(BaseModel):
280
277
  changelog_relationship.add_property(
281
278
  name="is_protected", value_current=relationship.is_protected, value_previous=None
282
279
  )
283
- changelog_relationship.add_property(
284
- name="is_visible", value_current=relationship.is_visible, value_previous=None
285
- )
286
280
  self.relationships[changelog_relationship.name] = changelog_relationship
287
281
  elif relationship.schema.cardinality == RelationshipCardinality.MANY:
288
282
  if relationship.schema.name not in self.relationships:
@@ -340,7 +334,6 @@ class NodeChangelog(BaseModel):
340
334
  if owner_id := getattr(attribute, "owner_id", None):
341
335
  changelog_attribute.add_property(name="owner", value_current=owner_id, value_previous=None)
342
336
  changelog_attribute.add_property(name="is_protected", value_current=attribute.is_protected, value_previous=None)
343
- changelog_attribute.add_property(name="is_visible", value_current=attribute.is_visible, value_previous=None)
344
337
  self.attributes[changelog_attribute.name] = changelog_attribute
345
338
 
346
339
  def get_related_nodes(self) -> list[ChangelogRelatedNode]:
@@ -46,6 +46,8 @@ NULL_VALUE = "NULL"
46
46
 
47
47
  EVENT_NAMESPACE = "infrahub"
48
48
 
49
+ SYSTEM_USER_ID = "__system__"
50
+
49
51
 
50
52
  class EventType(InfrahubStringEnum):
51
53
  BRANCH_CREATED = f"{EVENT_NAMESPACE}.branch.created"
@@ -359,6 +361,21 @@ class AttributeDBNodeType(Flag):
359
361
  IPNETWORK = DEFAULT | INDEX_ONLY | IPNETWORK_ONLY
360
362
 
361
363
 
364
+ class MetadataOptions(Flag):
365
+ NONE = 0
366
+ SOURCE = auto()
367
+ OWNER = auto()
368
+ LINKED_NODES = SOURCE | OWNER
369
+ IS_PROTECTED = auto()
370
+ CREATED_BY = auto()
371
+ CREATED_AT = auto()
372
+ UPDATED_BY = auto()
373
+ UPDATED_AT = auto()
374
+ TIMESTAMPS = CREATED_AT | UPDATED_AT
375
+ USERS = CREATED_BY | UPDATED_BY
376
+ USER_TIMESTAMPS = TIMESTAMPS | USERS
377
+
378
+
362
379
  RESTRICTED_NAMESPACES: list[str] = [
363
380
  "Account",
364
381
  "Branch",
@@ -8,7 +8,6 @@ class DatabaseEdgeType(Enum):
8
8
  HAS_ATTRIBUTE = "HAS_ATTRIBUTE"
9
9
  IS_RELATED = "IS_RELATED"
10
10
  HAS_VALUE = "HAS_VALUE"
11
- IS_VISIBLE = "IS_VISIBLE"
12
11
  IS_PROTECTED = "IS_PROTECTED"
13
12
  HAS_OWNER = "HAS_OWNER"
14
13
  HAS_SOURCE = "HAS_SOURCE"
@@ -4,7 +4,6 @@ PARENT_CHILD_IDENTIFIER = "parent__child"
4
4
 
5
5
 
6
6
  class FlagProperty(Enum):
7
- IS_VISIBLE = "is_visible"
8
7
  IS_PROTECTED = "is_protected"
9
8
 
10
9
 
@@ -1,6 +1,5 @@
1
1
  from infrahub import lock
2
2
  from infrahub.core.branch import Branch
3
- from infrahub.core.constants.infrahubkind import REPOSITORYVALIDATOR, USERVALIDATOR
4
3
  from infrahub.core.convert_object_type.object_conversion import (
5
4
  ConversionFieldInput,
6
5
  convert_object_type,
@@ -8,7 +7,7 @@ from infrahub.core.convert_object_type.object_conversion import (
8
7
  )
9
8
  from infrahub.core.manager import NodeManager
10
9
  from infrahub.core.node import Node
11
- from infrahub.core.protocols import CoreReadOnlyRepository, CoreRepository
10
+ from infrahub.core.protocols import CoreReadOnlyRepository, CoreRepository, CoreRepositoryValidator, CoreUserValidator
12
11
  from infrahub.core.schema import NodeSchema
13
12
  from infrahub.core.timestamp import Timestamp
14
13
  from infrahub.database import InfrahubDatabase
@@ -35,11 +34,11 @@ async def convert_repository_type(
35
34
 
36
35
  # Fetch validators before deleting the repository otherwise validator-repository would no longer exist
37
36
  user_validators = await NodeManager.query(
38
- db=dbt, schema=USERVALIDATOR, prefetch_relationships=True, filters={"repository__id": repository.id}
37
+ db=dbt, schema=CoreUserValidator, prefetch_relationships=True, filters={"repository__id": repository.id}
39
38
  )
40
39
  repository_validators = await NodeManager.query(
41
40
  db=dbt,
42
- schema=REPOSITORYVALIDATOR,
41
+ schema=CoreRepositoryValidator,
43
42
  prefetch_relationships=True,
44
43
  filters={"repository__id": repository.id},
45
44
  )
@@ -41,7 +41,7 @@ class BranchDiffer:
41
41
  diff_to: str | Timestamp | None = None,
42
42
  db: InfrahubDatabase | None = None,
43
43
  service: InfrahubServices | None = None,
44
- ):
44
+ ) -> None:
45
45
  """_summary_
46
46
 
47
47
  Args:
@@ -9,7 +9,7 @@ from .model.path import (
9
9
 
10
10
 
11
11
  class DiffConflictTransferer:
12
- def __init__(self, diff_combiner: DiffCombiner):
12
+ def __init__(self, diff_combiner: DiffCombiner) -> None:
13
13
  self.diff_combiner = diff_combiner
14
14
 
15
15
  async def transfer(self, earlier: EnrichedDiffRoot, later: EnrichedDiffRoot) -> None:
@@ -1,10 +1,11 @@
1
1
  from enum import Enum
2
2
 
3
- from infrahub.core.constants import BranchConflictKeep, InfrahubKind
3
+ from infrahub.core.constants import BranchConflictKeep
4
4
  from infrahub.core.diff.query.filters import EnrichedDiffQueryFilters
5
5
  from infrahub.core.integrity.object_conflict.conflict_recorder import ObjectConflictValidatorRecorder
6
6
  from infrahub.core.manager import NodeManager
7
7
  from infrahub.core.node import Node
8
+ from infrahub.core.protocols import CoreProposedChange
8
9
  from infrahub.database import InfrahubDatabase
9
10
  from infrahub.exceptions import SchemaNotFoundError
10
11
  from infrahub.proposed_change.constants import ProposedChangeState
@@ -28,7 +29,7 @@ class DiffDataCheckSynchronizer:
28
29
  conflicts_extractor: DiffConflictsExtractor,
29
30
  conflict_recorder: ObjectConflictValidatorRecorder,
30
31
  diff_repository: DiffRepository,
31
- ):
32
+ ) -> None:
32
33
  self.db = db
33
34
  self.conflicts_extractor = conflicts_extractor
34
35
  self.conflict_recorder = conflict_recorder
@@ -52,7 +53,7 @@ class DiffDataCheckSynchronizer:
52
53
  try:
53
54
  proposed_changes = await NodeManager.query(
54
55
  db=self.db,
55
- schema=InfrahubKind.PROPOSEDCHANGE,
56
+ schema=CoreProposedChange,
56
57
  filters={"source_branch": enriched_diff.diff_branch_name, "state": ProposedChangeState.OPEN},
57
58
  )
58
59
  except SchemaNotFoundError:
@@ -28,10 +28,10 @@ class DiffCardinalityOneEnricher(DiffEnricherInterface):
28
28
  - the peer_id property of the element will be the latest non-null peer ID for this element
29
29
  - the element MUST have an EnrichedDiffProperty of property_type=IS_RELATED that correctly records
30
30
  the previous and new values of the peer ID for this element
31
- - changes to properties (IS_VISIBLE, etc) of a cardinality=one relationship are consolidated as well
31
+ - changes to properties (IS_PROTECTED, etc) of a cardinality=one relationship are consolidated as well
32
32
  """
33
33
 
34
- def __init__(self, db: InfrahubDatabase):
34
+ def __init__(self, db: InfrahubDatabase) -> None:
35
35
  self.db = db
36
36
  self._node_schema_map: dict[str, MainSchemaTypes] = {}
37
37
 
@@ -24,7 +24,7 @@ log = get_logger()
24
24
  class DiffHierarchyEnricher(DiffEnricherInterface):
25
25
  """Add hierarchy and parent/component nodes to diff even if the higher-level nodes are unchanged"""
26
26
 
27
- def __init__(self, db: InfrahubDatabase, parent_adder: DiffParentNodeAdder):
27
+ def __init__(self, db: InfrahubDatabase, parent_adder: DiffParentNodeAdder) -> None:
28
28
  self.db = db
29
29
  self.parent_adder = parent_adder
30
30
 
@@ -35,7 +35,7 @@ class DisplayLabelRequest:
35
35
  class DiffLabelsEnricher(DiffEnricherInterface):
36
36
  """Add display labels for nodes and labels for relationships"""
37
37
 
38
- def __init__(self, db: InfrahubDatabase):
38
+ def __init__(self, db: InfrahubDatabase) -> None:
39
39
  self.db = db
40
40
  self._base_branch_name: str | None = None
41
41
  self._diff_branch_name: str | None = None
@@ -6,6 +6,7 @@ from infrahub.core import registry
6
6
  from infrahub.core.constants import DiffAction
7
7
  from infrahub.core.diff.model.path import BranchTrackingId
8
8
  from infrahub.core.diff.query.merge import (
9
+ DiffMergeMetadataQuery,
9
10
  DiffMergeMigratedKindsQuery,
10
11
  DiffMergePropertiesQuery,
11
12
  DiffMergeQuery,
@@ -26,6 +27,8 @@ log = get_logger()
26
27
 
27
28
 
28
29
  class DiffMerger:
30
+ metadata_batch_size = 500
31
+
29
32
  def __init__(
30
33
  self,
31
34
  db: InfrahubDatabase,
@@ -33,12 +36,13 @@ class DiffMerger:
33
36
  destination_branch: Branch,
34
37
  diff_repository: DiffRepository,
35
38
  serializer: DiffMergeSerializer,
36
- ):
39
+ ) -> None:
37
40
  self.source_branch = source_branch
38
41
  self.destination_branch = destination_branch
39
42
  self.db = db
40
43
  self.diff_repository = diff_repository
41
44
  self.serializer = serializer
45
+ self._affected_node_uuids: list[str] = []
42
46
 
43
47
  async def merge_graph(self, at: Timestamp) -> EnrichedDiffRoot:
44
48
  tracking_id = BranchTrackingId(name=self.source_branch.name)
@@ -69,6 +73,7 @@ class DiffMerger:
69
73
  # make sure that we use the ADDED db_id if it exists
70
74
  # it will not if a node was migrated and then deleted
71
75
  migrated_kinds_id_map[n.uuid] = n.identifier.db_id
76
+
72
77
  async for node_diff_dicts, property_diff_dicts in self.serializer.serialize_diff(diff=enriched_diff):
73
78
  if node_diff_dicts:
74
79
  log.info(f"Merging batch of nodes #{batch_num}")
@@ -105,13 +110,34 @@ class DiffMerger:
105
110
  )
106
111
  await migrated_merge_query.execute(db=self.db)
107
112
 
113
+ affected_node_uuids = [n.uuid for n in enriched_diff.nodes]
114
+ self._affected_node_uuids = affected_node_uuids
115
+ if affected_node_uuids:
116
+ for i in range(0, len(affected_node_uuids), self.metadata_batch_size):
117
+ batch_uuids = affected_node_uuids[i : i + self.metadata_batch_size]
118
+ log.info(f"Updating metadata for batch {i // self.metadata_batch_size + 1} ({len(batch_uuids)} nodes)")
119
+ metadata_query = await DiffMergeMetadataQuery.init(
120
+ db=self.db,
121
+ branch=self.source_branch,
122
+ at=at,
123
+ target_branch=self.destination_branch,
124
+ node_uuids=batch_uuids,
125
+ )
126
+ await metadata_query.execute(db=self.db)
127
+
108
128
  self.source_branch.branched_from = at.to_string()
109
129
  await self.source_branch.save(db=self.db)
110
130
  registry.branch[self.source_branch.name] = self.source_branch
111
131
  return enriched_diff
112
132
 
113
133
  async def rollback(self, at: Timestamp) -> None:
134
+ if not self._affected_node_uuids:
135
+ return
114
136
  rollback_query = await DiffMergeRollbackQuery.init(
115
- db=self.db, branch=self.source_branch, target_branch=self.destination_branch, at=at
137
+ db=self.db,
138
+ branch=self.source_branch,
139
+ target_branch=self.destination_branch,
140
+ at=at,
141
+ node_uuids=self._affected_node_uuids,
116
142
  )
117
143
  await rollback_query.execute(db=self.db)
@@ -93,9 +93,7 @@ class DiffMergeSerializer:
93
93
  if property_type in (DatabaseEdgeType.HAS_OWNER, DatabaseEdgeType.HAS_SOURCE, DatabaseEdgeType.IS_RELATED):
94
94
  return raw_value
95
95
  # these are boolean
96
- if (property_type in (DatabaseEdgeType.IS_VISIBLE, DatabaseEdgeType.IS_PROTECTED)) and isinstance(
97
- raw_value, str
98
- ):
96
+ if property_type == DatabaseEdgeType.IS_PROTECTED and isinstance(raw_value, str):
99
97
  return raw_value.lower() == "true"
100
98
  # this must be HAS_VALUE
101
99
  if raw_value in (None, NULL_VALUE):
@@ -249,13 +247,8 @@ class DiffMergeSerializer:
249
247
  return attr_dict, attr_prop_dict
250
248
 
251
249
  def _get_default_property_merge_dicts(self, action: DiffAction) -> dict[DatabaseEdgeType, PropertyMergeDict]:
252
- # start with default values for IS_VISIBLE and IS_PROTECTED b/c we always want to update them during a merge
250
+ # start with default values for IS_PROTECTED b/c we always want to update them during a merge
253
251
  return {
254
- DatabaseEdgeType.IS_VISIBLE: PropertyMergeDict(
255
- property_type=DatabaseEdgeType.IS_VISIBLE.value,
256
- action=self._to_action_str(action),
257
- value=None,
258
- ),
259
252
  DatabaseEdgeType.IS_PROTECTED: PropertyMergeDict(
260
253
  property_type=DatabaseEdgeType.IS_PROTECTED.value,
261
254
  action=self._to_action_str(action),
@@ -335,7 +328,7 @@ class DiffMergeSerializer:
335
328
  # handled above
336
329
  continue
337
330
  python_value_type: type = str
338
- if property_diff.property_type in (DatabaseEdgeType.IS_VISIBLE, DatabaseEdgeType.IS_PROTECTED):
331
+ if property_diff.property_type is DatabaseEdgeType.IS_PROTECTED:
339
332
  python_value_type = bool
340
333
  actions_and_values = self._get_property_actions_and_values(
341
334
  property_diff=property_diff, python_value_type=python_value_type
@@ -26,7 +26,7 @@ class BaseDiffElement(BaseModel):
26
26
  """
27
27
  resp: dict[str, Any] = {}
28
28
  for key, value in self:
29
- field_info = self.model_fields[key]
29
+ field_info = self.__class__.model_fields[key]
30
30
  if isinstance(value, BaseModel):
31
31
  resp[key] = value.to_graphql() # type: ignore[attr-defined]
32
32
  elif isinstance(value, dict):