infrahub-server 1.7.0b0__py3-none-any.whl → 1.7.1__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 (177) hide show
  1. infrahub/api/exceptions.py +2 -2
  2. infrahub/api/schema.py +5 -0
  3. infrahub/cli/db.py +54 -24
  4. infrahub/core/account.py +12 -9
  5. infrahub/core/branch/models.py +11 -117
  6. infrahub/core/branch/tasks.py +7 -3
  7. infrahub/core/diff/branch_differ.py +1 -1
  8. infrahub/core/diff/conflict_transferer.py +1 -1
  9. infrahub/core/diff/data_check_synchronizer.py +1 -1
  10. infrahub/core/diff/enricher/cardinality_one.py +1 -1
  11. infrahub/core/diff/enricher/hierarchy.py +1 -1
  12. infrahub/core/diff/enricher/labels.py +1 -1
  13. infrahub/core/diff/merger/merger.py +6 -2
  14. infrahub/core/diff/repository/repository.py +3 -1
  15. infrahub/core/graph/__init__.py +1 -1
  16. infrahub/core/graph/constraints.py +1 -1
  17. infrahub/core/initialization.py +2 -1
  18. infrahub/core/ipam/reconciler.py +8 -6
  19. infrahub/core/ipam/utilization.py +8 -15
  20. infrahub/core/manager.py +1 -26
  21. infrahub/core/merge.py +1 -1
  22. infrahub/core/migrations/graph/__init__.py +2 -0
  23. infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -12
  24. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +4 -4
  25. infrahub/core/migrations/graph/m014_remove_index_attr_value.py +3 -2
  26. infrahub/core/migrations/graph/m015_diff_format_update.py +3 -2
  27. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +3 -2
  28. infrahub/core/migrations/graph/m017_add_core_profile.py +6 -4
  29. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +3 -4
  30. infrahub/core/migrations/graph/m020_duplicate_edges.py +3 -3
  31. infrahub/core/migrations/graph/m025_uniqueness_nulls.py +3 -4
  32. infrahub/core/migrations/graph/m026_0000_prefix_fix.py +4 -5
  33. infrahub/core/migrations/graph/m028_delete_diffs.py +3 -2
  34. infrahub/core/migrations/graph/m029_duplicates_cleanup.py +3 -2
  35. infrahub/core/migrations/graph/m031_check_number_attributes.py +4 -3
  36. infrahub/core/migrations/graph/m032_cleanup_orphaned_branch_relationships.py +3 -2
  37. infrahub/core/migrations/graph/m034_find_orphaned_schema_fields.py +3 -2
  38. infrahub/core/migrations/graph/m035_orphan_relationships.py +3 -3
  39. infrahub/core/migrations/graph/m036_drop_attr_value_index.py +3 -2
  40. infrahub/core/migrations/graph/m037_index_attr_vals.py +3 -2
  41. infrahub/core/migrations/graph/m038_redo_0000_prefix_fix.py +4 -5
  42. infrahub/core/migrations/graph/m039_ipam_reconcile.py +3 -2
  43. infrahub/core/migrations/graph/m041_deleted_dup_edges.py +4 -3
  44. infrahub/core/migrations/graph/m042_profile_attrs_in_db.py +5 -4
  45. infrahub/core/migrations/graph/m043_create_hfid_display_label_in_db.py +12 -5
  46. infrahub/core/migrations/graph/m044_backfill_hfid_display_label_in_db.py +15 -4
  47. infrahub/core/migrations/graph/m045_backfill_hfid_display_label_in_db_profile_template.py +10 -4
  48. infrahub/core/migrations/graph/m046_fill_agnostic_hfid_display_labels.py +6 -5
  49. infrahub/core/migrations/graph/m047_backfill_or_null_display_label.py +19 -5
  50. infrahub/core/migrations/graph/m048_undelete_rel_props.py +6 -4
  51. infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +19 -4
  52. infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +3 -3
  53. infrahub/core/migrations/graph/m051_subtract_branched_from_microsecond.py +39 -0
  54. infrahub/core/migrations/query/__init__.py +2 -2
  55. infrahub/core/migrations/query/schema_attribute_update.py +1 -1
  56. infrahub/core/migrations/runner.py +6 -3
  57. infrahub/core/migrations/schema/attribute_kind_update.py +8 -11
  58. infrahub/core/migrations/schema/attribute_name_update.py +1 -1
  59. infrahub/core/migrations/schema/attribute_supports_profile.py +5 -10
  60. infrahub/core/migrations/schema/models.py +8 -0
  61. infrahub/core/migrations/schema/node_attribute_add.py +11 -14
  62. infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
  63. infrahub/core/migrations/schema/node_kind_update.py +1 -1
  64. infrahub/core/migrations/schema/tasks.py +7 -1
  65. infrahub/core/migrations/shared.py +37 -30
  66. infrahub/core/node/__init__.py +3 -2
  67. infrahub/core/node/base.py +9 -5
  68. infrahub/core/node/delete_validator.py +1 -1
  69. infrahub/core/order.py +30 -0
  70. infrahub/core/protocols.py +1 -0
  71. infrahub/core/protocols_base.py +4 -0
  72. infrahub/core/query/__init__.py +8 -5
  73. infrahub/core/query/attribute.py +3 -3
  74. infrahub/core/query/branch.py +1 -1
  75. infrahub/core/query/delete.py +1 -1
  76. infrahub/core/query/diff.py +3 -3
  77. infrahub/core/query/ipam.py +104 -43
  78. infrahub/core/query/node.py +454 -101
  79. infrahub/core/query/relationship.py +83 -26
  80. infrahub/core/query/resource_manager.py +107 -18
  81. infrahub/core/relationship/constraints/count.py +1 -1
  82. infrahub/core/relationship/constraints/peer_kind.py +1 -1
  83. infrahub/core/relationship/constraints/peer_parent.py +1 -1
  84. infrahub/core/relationship/constraints/peer_relatives.py +1 -1
  85. infrahub/core/relationship/constraints/profiles_kind.py +1 -1
  86. infrahub/core/relationship/constraints/profiles_removal.py +1 -1
  87. infrahub/core/relationship/model.py +8 -2
  88. infrahub/core/schema/attribute_parameters.py +28 -1
  89. infrahub/core/schema/attribute_schema.py +9 -15
  90. infrahub/core/schema/basenode_schema.py +3 -0
  91. infrahub/core/schema/definitions/core/__init__.py +8 -2
  92. infrahub/core/schema/definitions/core/account.py +10 -10
  93. infrahub/core/schema/definitions/core/artifact.py +14 -8
  94. infrahub/core/schema/definitions/core/check.py +10 -4
  95. infrahub/core/schema/definitions/core/generator.py +26 -6
  96. infrahub/core/schema/definitions/core/graphql_query.py +1 -1
  97. infrahub/core/schema/definitions/core/group.py +9 -2
  98. infrahub/core/schema/definitions/core/ipam.py +80 -10
  99. infrahub/core/schema/definitions/core/menu.py +41 -7
  100. infrahub/core/schema/definitions/core/permission.py +16 -2
  101. infrahub/core/schema/definitions/core/profile.py +16 -2
  102. infrahub/core/schema/definitions/core/propose_change.py +24 -4
  103. infrahub/core/schema/definitions/core/propose_change_comment.py +23 -11
  104. infrahub/core/schema/definitions/core/propose_change_validator.py +50 -21
  105. infrahub/core/schema/definitions/core/repository.py +10 -0
  106. infrahub/core/schema/definitions/core/resource_pool.py +8 -1
  107. infrahub/core/schema/definitions/core/template.py +19 -2
  108. infrahub/core/schema/definitions/core/transform.py +11 -5
  109. infrahub/core/schema/definitions/core/webhook.py +27 -9
  110. infrahub/core/schema/manager.py +50 -38
  111. infrahub/core/schema/schema_branch.py +68 -2
  112. infrahub/core/utils.py +3 -3
  113. infrahub/core/validators/aggregated_checker.py +1 -1
  114. infrahub/core/validators/attribute/choices.py +1 -1
  115. infrahub/core/validators/attribute/enum.py +1 -1
  116. infrahub/core/validators/attribute/kind.py +6 -3
  117. infrahub/core/validators/attribute/length.py +1 -1
  118. infrahub/core/validators/attribute/min_max.py +1 -1
  119. infrahub/core/validators/attribute/number_pool.py +1 -1
  120. infrahub/core/validators/attribute/optional.py +1 -1
  121. infrahub/core/validators/attribute/regex.py +1 -1
  122. infrahub/core/validators/node/attribute.py +1 -1
  123. infrahub/core/validators/node/relationship.py +1 -1
  124. infrahub/core/validators/relationship/peer.py +1 -1
  125. infrahub/database/__init__.py +1 -1
  126. infrahub/git/utils.py +1 -1
  127. infrahub/graphql/app.py +2 -2
  128. infrahub/graphql/field_extractor.py +1 -1
  129. infrahub/graphql/manager.py +17 -3
  130. infrahub/graphql/mutations/account.py +1 -1
  131. infrahub/graphql/order.py +14 -0
  132. infrahub/graphql/queries/diff/tree.py +5 -5
  133. infrahub/graphql/queries/resource_manager.py +25 -24
  134. infrahub/graphql/resolvers/ipam.py +3 -3
  135. infrahub/graphql/resolvers/resolver.py +44 -3
  136. infrahub/graphql/types/standard_node.py +8 -4
  137. infrahub/lock.py +7 -0
  138. infrahub/menu/repository.py +1 -1
  139. infrahub/patch/queries/base.py +1 -1
  140. infrahub/pools/number.py +1 -8
  141. infrahub/profiles/node_applier.py +1 -1
  142. infrahub/profiles/queries/get_profile_data.py +1 -1
  143. infrahub/proposed_change/action_checker.py +1 -1
  144. infrahub/services/__init__.py +1 -1
  145. infrahub/services/adapters/cache/nats.py +1 -1
  146. infrahub/services/adapters/cache/redis.py +7 -0
  147. infrahub/webhook/gather.py +1 -1
  148. infrahub/webhook/tasks.py +22 -6
  149. infrahub_sdk/analyzer.py +2 -2
  150. infrahub_sdk/branch.py +12 -39
  151. infrahub_sdk/checks.py +4 -4
  152. infrahub_sdk/client.py +36 -0
  153. infrahub_sdk/ctl/cli_commands.py +2 -1
  154. infrahub_sdk/ctl/graphql.py +15 -4
  155. infrahub_sdk/ctl/utils.py +2 -2
  156. infrahub_sdk/enums.py +6 -0
  157. infrahub_sdk/graphql/renderers.py +21 -0
  158. infrahub_sdk/graphql/utils.py +85 -0
  159. infrahub_sdk/node/attribute.py +12 -2
  160. infrahub_sdk/node/constants.py +11 -0
  161. infrahub_sdk/node/metadata.py +69 -0
  162. infrahub_sdk/node/node.py +65 -14
  163. infrahub_sdk/node/property.py +3 -0
  164. infrahub_sdk/node/related_node.py +24 -1
  165. infrahub_sdk/node/relationship.py +10 -1
  166. infrahub_sdk/operation.py +2 -2
  167. infrahub_sdk/schema/repository.py +1 -2
  168. infrahub_sdk/transforms.py +2 -2
  169. infrahub_sdk/types.py +18 -2
  170. {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.1.dist-info}/METADATA +6 -6
  171. {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.1.dist-info}/RECORD +176 -172
  172. {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.1.dist-info}/entry_points.txt +0 -1
  173. infrahub_testcontainers/models.py +3 -3
  174. infrahub_testcontainers/performance_test.py +1 -1
  175. infrahub/graphql/models.py +0 -36
  176. {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.1.dist-info}/WHEEL +0 -0
  177. {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.1.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from infrahub.core.branch import Branch
6
+ from infrahub.core.migrations.shared import ArbitraryMigration, MigrationInput, MigrationResult
7
+ from infrahub.core.timestamp import Timestamp
8
+
9
+ if TYPE_CHECKING:
10
+ from infrahub.database import InfrahubDatabase
11
+
12
+
13
+ class Migration051(ArbitraryMigration):
14
+ name: str = "051_subtract_branched_from_microsecond"
15
+ minimum_version: int = 50
16
+
17
+ async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult: # noqa: ARG002
18
+ return MigrationResult()
19
+
20
+ async def execute(self, migration_input: MigrationInput) -> MigrationResult:
21
+ db = migration_input.db
22
+ result = MigrationResult()
23
+
24
+ branches = await Branch.get_list(db=db)
25
+
26
+ for branch in branches:
27
+ if branch.is_default or branch.is_global:
28
+ continue
29
+
30
+ if not branch.branched_from:
31
+ continue
32
+
33
+ original_ts = Timestamp(branch.branched_from)
34
+ new_ts = original_ts.subtract(microseconds=1)
35
+ branch.branched_from = new_ts.to_string()
36
+
37
+ await branch.save(db=db)
38
+
39
+ return result
@@ -20,7 +20,7 @@ class MigrationQuery(MigrationBaseQuery):
20
20
  self,
21
21
  migration: SchemaMigration,
22
22
  **kwargs: Any,
23
- ):
23
+ ) -> None:
24
24
  self.migration = migration
25
25
  super().__init__(**kwargs)
26
26
 
@@ -32,6 +32,6 @@ class AttributeMigrationQuery(MigrationBaseQuery):
32
32
  self,
33
33
  migration: AttributeSchemaMigration,
34
34
  **kwargs: Any,
35
- ):
35
+ ) -> None:
36
36
  self.migration = migration
37
37
  super().__init__(**kwargs)
@@ -24,7 +24,7 @@ class SchemaAttributeUpdateQuery(Query):
24
24
  new_value: Any,
25
25
  previous_value: Any | None = None,
26
26
  **kwargs: Any,
27
- ):
27
+ ) -> None:
28
28
  self.attr_name = attribute_name
29
29
  self.node_name = node_name
30
30
  self.node_namespace = node_namespace
@@ -7,10 +7,11 @@ from infrahub.core.constants import GLOBAL_BRANCH_NAME
7
7
  from infrahub.core.migrations.graph import MIGRATIONS
8
8
 
9
9
  from .exceptions import MigrationFailureError
10
- from .shared import MigrationRequiringRebase
10
+ from .shared import MigrationInput, MigrationRequiringRebase
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from infrahub.core.branch import Branch
14
+ from infrahub.core.timestamp import Timestamp
14
15
  from infrahub.database import InfrahubDatabase
15
16
 
16
17
 
@@ -35,12 +36,14 @@ class MigrationRunner:
35
36
  def has_migrations(self) -> bool:
36
37
  return bool(self.applicable_migrations)
37
38
 
38
- async def run(self, db: InfrahubDatabase) -> None:
39
+ async def run(self, db: InfrahubDatabase, at: Timestamp) -> None:
39
40
  if not self.has_migrations():
40
41
  return
41
42
 
42
43
  for migration in self.applicable_migrations:
43
- execution_result = await migration.execute_against_branch(db=db, branch=self.branch)
44
+ execution_result = await migration.execute_against_branch(
45
+ migration_input=MigrationInput(db=db, at=at), branch=self.branch
46
+ )
44
47
  validation_result = None
45
48
 
46
49
  if execution_result.success:
@@ -2,15 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING, Any, Sequence
4
4
 
5
- from infrahub.core.constants import SYSTEM_USER_ID
6
5
  from infrahub.types import is_large_attribute_type
7
6
 
8
7
  from ..query import AttributeMigrationQuery, MigrationBaseQuery
9
- from ..shared import AttributeSchemaMigration, MigrationResult
8
+ from ..shared import AttributeSchemaMigration, MigrationInput, MigrationResult
10
9
 
11
10
  if TYPE_CHECKING:
12
11
  from infrahub.core.branch.models import Branch
13
- from infrahub.core.timestamp import Timestamp
14
12
  from infrahub.database import InfrahubDatabase
15
13
 
16
14
 
@@ -40,7 +38,7 @@ class AttributeKindUpdateMigrationQuery(AttributeMigrationQuery):
40
38
  // ------------
41
39
  // start with all the Attribute vertices we might care about
42
40
  // ------------
43
- MATCH (n:%(schema_kind)s)-[:HAS_ATTRIBUTE]->(attr:Attribute)
41
+ MATCH (n:%(schema_kinds)s)-[:HAS_ATTRIBUTE]->(attr:Attribute)
44
42
  WHERE attr.name = $attr_name
45
43
  WITH DISTINCT n, attr
46
44
 
@@ -76,7 +74,7 @@ CALL (av_is_default, av_value) {
76
74
  // ------------
77
75
  WITH 1 AS one
78
76
  LIMIT 1
79
- MATCH (n:%(schema_kind)s)-[:HAS_ATTRIBUTE]->(attr:Attribute)
77
+ MATCH (n:%(schema_kinds)s)-[:HAS_ATTRIBUTE]->(attr:Attribute)
80
78
  WHERE attr.name = $attr_name
81
79
  WITH DISTINCT n, attr
82
80
 
@@ -94,7 +92,6 @@ CALL (n, attr) {
94
92
  RETURN has_value_e, av
95
93
  }
96
94
 
97
-
98
95
  // ------------
99
96
  // create and update the HAS_VALUE edges
100
97
  // ------------
@@ -154,7 +151,9 @@ CALL (attr, n) {
154
151
  SET n.updated_at = $at, n.updated_by = $user_id
155
152
  }
156
153
  """ % {
157
- "schema_kind": self.migration.previous_schema.kind,
154
+ "schema_kinds": (
155
+ f"{self.migration.previous_schema.kind}|Profile{self.migration.previous_schema.kind}|Template{self.migration.previous_schema.kind}"
156
+ ),
158
157
  "branch_filter": branch_filter,
159
158
  "new_attr_value_labels": new_attr_value_labels,
160
159
  }
@@ -167,15 +166,13 @@ class AttributeKindUpdateMigration(AttributeSchemaMigration):
167
166
 
168
167
  async def execute(
169
168
  self,
170
- db: InfrahubDatabase,
169
+ migration_input: MigrationInput,
171
170
  branch: Branch,
172
- at: Timestamp | str | None = None,
173
171
  queries: Sequence[type[MigrationBaseQuery]] | None = None,
174
- user_id: str = SYSTEM_USER_ID,
175
172
  ) -> MigrationResult:
176
173
  is_indexed_previous = is_large_attribute_type(self.previous_attribute_schema.kind)
177
174
  is_indexed_new = is_large_attribute_type(self.new_attribute_schema.kind)
178
175
  if is_indexed_previous is is_indexed_new:
179
176
  return MigrationResult()
180
177
 
181
- return await super().execute(db=db, branch=branch, at=at, queries=queries, user_id=user_id)
178
+ return await super().execute(migration_input=migration_input, branch=branch, queries=queries)
@@ -14,7 +14,7 @@ class AttributeNameUpdateMigrationQuery01(AttributeMigrationQuery, AttributeRena
14
14
  self,
15
15
  migration: AttributeSchemaMigration,
16
16
  **kwargs: Any,
17
- ):
17
+ ) -> None:
18
18
  new_attr = AttributeInfo(
19
19
  name=migration.new_attribute_schema.name,
20
20
  node_kind=migration.new_schema.kind,
@@ -2,20 +2,17 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING, Any, Sequence
4
4
 
5
- from infrahub.core.constants import SYSTEM_USER_ID
6
5
  from infrahub.core.migrations.query.attribute_remove import AttributeRemoveQuery
7
6
  from infrahub.core.schema.generic_schema import GenericSchema
8
7
  from infrahub.core.schema.node_schema import NodeSchema
9
8
 
10
9
  from ..query import AttributeMigrationQuery, MigrationBaseQuery
11
10
  from ..query.attribute_add import AttributeAddQuery
12
- from ..shared import AttributeSchemaMigration, MigrationResult
11
+ from ..shared import AttributeSchemaMigration, MigrationInput, MigrationResult
13
12
 
14
13
  if TYPE_CHECKING:
15
14
  from infrahub.core.branch.models import Branch
16
15
  from infrahub.core.schema import MainSchemaTypes
17
- from infrahub.core.timestamp import Timestamp
18
- from infrahub.database import InfrahubDatabase
19
16
 
20
17
 
21
18
  def _get_node_kinds(schema: MainSchemaTypes) -> list[str]:
@@ -34,7 +31,7 @@ class ProfilesAttributeAddMigrationQuery(AttributeMigrationQuery, AttributeAddQu
34
31
  self,
35
32
  migration: AttributeSchemaMigration,
36
33
  **kwargs: Any,
37
- ):
34
+ ) -> None:
38
35
  node_kinds = _get_node_kinds(migration.new_schema)
39
36
  super().__init__(
40
37
  migration=migration,
@@ -54,7 +51,7 @@ class ProfilesAttributeRemoveMigrationQuery(AttributeMigrationQuery, AttributeRe
54
51
  self,
55
52
  migration: AttributeSchemaMigration,
56
53
  **kwargs: Any,
57
- ):
54
+ ) -> None:
58
55
  node_kinds = _get_node_kinds(migration.new_schema)
59
56
  super().__init__(
60
57
  migration=migration,
@@ -70,11 +67,9 @@ class AttributeSupportsProfileUpdateMigration(AttributeSchemaMigration):
70
67
 
71
68
  async def execute(
72
69
  self,
73
- db: InfrahubDatabase,
70
+ migration_input: MigrationInput,
74
71
  branch: Branch,
75
- at: Timestamp | str | None = None,
76
72
  queries: Sequence[type[MigrationBaseQuery]] | None = None, # noqa: ARG002
77
- user_id: str = SYSTEM_USER_ID,
78
73
  ) -> MigrationResult:
79
74
  if (
80
75
  # no change in whether the attribute should be used on profiles
@@ -89,4 +84,4 @@ class AttributeSupportsProfileUpdateMigration(AttributeSchemaMigration):
89
84
  if not self.new_attribute_schema.support_profiles:
90
85
  profiles_queries.append(ProfilesAttributeRemoveMigrationQuery)
91
86
 
92
- return await super().execute(db=db, branch=branch, at=at, queries=profiles_queries, user_id=user_id)
87
+ return await super().execute(migration_input=migration_input, branch=branch, queries=profiles_queries)
@@ -7,6 +7,7 @@ from infrahub.core.constants import SYSTEM_USER_ID
7
7
  from infrahub.core.models import SchemaUpdateMigrationInfo
8
8
  from infrahub.core.path import SchemaPath
9
9
  from infrahub.core.schema.schema_branch import SchemaBranch
10
+ from infrahub.core.timestamp import Timestamp
10
11
 
11
12
 
12
13
  class SchemaApplyMigrationData(BaseModel):
@@ -16,6 +17,7 @@ class SchemaApplyMigrationData(BaseModel):
16
17
  new_schema: SchemaBranch
17
18
  previous_schema: SchemaBranch
18
19
  migrations: list[SchemaUpdateMigrationInfo]
20
+ at: Timestamp
19
21
  user_id: str = SYSTEM_USER_ID
20
22
 
21
23
  @model_serializer()
@@ -26,6 +28,7 @@ class SchemaApplyMigrationData(BaseModel):
26
28
  "new_schema": self.new_schema.to_dict_schema_object(),
27
29
  "migrations": [migration.model_dump() for migration in self.migrations],
28
30
  "user_id": self.user_id,
31
+ "at": self.at.to_string(),
29
32
  }
30
33
 
31
34
  @field_validator("new_schema", "previous_schema", mode="before")
@@ -33,6 +36,11 @@ class SchemaApplyMigrationData(BaseModel):
33
36
  def validate_schema_branch(cls, value: Any) -> SchemaBranch:
34
37
  return SchemaBranch.validate(data=value)
35
38
 
39
+ @field_validator("at", mode="before")
40
+ @classmethod
41
+ def validate_at(cls, value: Any) -> Timestamp:
42
+ return Timestamp(value)
43
+
36
44
 
37
45
  class SchemaMigrationPathResponseData(BaseModel):
38
46
  errors: list[str] = Field(default_factory=list)
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  from typing import TYPE_CHECKING, Any, Sequence
4
4
 
5
5
  from infrahub.core import registry
6
- from infrahub.core.constants import SYSTEM_USER_ID
7
6
  from infrahub.core.node import Node
8
7
  from infrahub.core.schema.generic_schema import GenericSchema
9
8
  from infrahub.core.schema.node_schema import NodeSchema
@@ -12,16 +11,14 @@ from infrahub.tasks.registry import update_branch_registry
12
11
 
13
12
  from ..query import AttributeMigrationQuery, MigrationBaseQuery
14
13
  from ..query.attribute_add import AttributeAddQuery
15
- from ..shared import AttributeSchemaMigration, MigrationResult
14
+ from ..shared import AttributeSchemaMigration, MigrationInput, MigrationResult
16
15
 
17
16
  if TYPE_CHECKING:
18
17
  from infrahub.core.node.resource_manager.number_pool import CoreNumberPool
19
18
  from infrahub.core.schema import MainSchemaTypes
20
19
  from infrahub.core.schema.attribute_schema import AttributeSchema
21
- from infrahub.database import InfrahubDatabase
22
20
 
23
21
  from ...branch import Branch
24
- from ...timestamp import Timestamp
25
22
 
26
23
 
27
24
  class NodeAttributeAddMigrationQuery01(AttributeMigrationQuery, AttributeAddQuery):
@@ -41,7 +38,7 @@ class NodeAttributeAddMigrationQuery01(AttributeMigrationQuery, AttributeAddQuer
41
38
  self,
42
39
  migration: AttributeSchemaMigration,
43
40
  **kwargs: Any,
44
- ):
41
+ ) -> None:
45
42
  node_kinds = self._get_node_kinds(
46
43
  schema=migration.new_schema, new_attribute_schema=migration.new_attribute_schema
47
44
  )
@@ -62,32 +59,32 @@ class NodeAttributeAddMigration(AttributeSchemaMigration):
62
59
 
63
60
  async def execute(
64
61
  self,
65
- db: InfrahubDatabase,
62
+ migration_input: MigrationInput,
66
63
  branch: Branch,
67
- at: Timestamp | str | None = None,
68
64
  queries: Sequence[type[MigrationBaseQuery]] | None = None,
69
- user_id: str = SYSTEM_USER_ID,
70
65
  ) -> MigrationResult:
71
66
  if self.new_attribute_schema.inherited is True:
72
67
  return MigrationResult()
73
- return await super().execute(db=db, branch=branch, at=at, queries=queries, user_id=user_id)
68
+ return await super().execute(migration_input=migration_input, branch=branch, queries=queries)
74
69
 
75
70
  async def execute_post_queries(
76
71
  self,
77
- db: InfrahubDatabase,
72
+ migration_input: MigrationInput,
78
73
  result: MigrationResult,
79
74
  branch: Branch,
80
- at: Timestamp, # noqa: ARG002
81
- user_id: str, # noqa: ARG002
82
75
  ) -> MigrationResult:
83
76
  if self.new_attribute_schema.kind != "NumberPool":
84
77
  return result
85
78
 
79
+ db = migration_input.db
80
+ at = migration_input.at
81
+
86
82
  number_pool: CoreNumberPool = await Node.fetch_or_create_number_pool(
87
83
  db=db,
88
84
  branch=branch,
89
85
  schema_node=self.new_schema, # type: ignore
90
86
  schema_attribute=self.new_attribute_schema,
87
+ at=at,
91
88
  )
92
89
 
93
90
  await update_branch_registry(db=db, branch=branch)
@@ -108,11 +105,11 @@ class NodeAttributeAddMigration(AttributeSchemaMigration):
108
105
  return result
109
106
 
110
107
  for node, number in zip(nodes, numbers, strict=True):
111
- await number_pool.reserve(db=db, number=number, identifier=node.get_id())
108
+ await number_pool.reserve(db=db, number=number, identifier=node.get_id(), at=at)
112
109
  attr = getattr(node, self.new_attribute_schema.name)
113
110
  attr.value = number
114
111
  attr.source = number_pool.id
115
112
 
116
- await node.save(db=db, fields=[self.new_attribute_schema.name])
113
+ await node.save(db=db, fields=[self.new_attribute_schema.name], at=at)
117
114
 
118
115
  return result
@@ -15,7 +15,7 @@ class NodeAttributeRemoveMigrationQuery01(AttributeMigrationQuery, AttributeRemo
15
15
  self,
16
16
  migration: AttributeSchemaMigration,
17
17
  **kwargs: Any,
18
- ):
18
+ ) -> None:
19
19
  super().__init__(
20
20
  migration=migration,
21
21
  attribute_name=migration.previous_attribute_schema.name,
@@ -14,7 +14,7 @@ class NodeKindUpdateMigrationQuery01(MigrationQuery, NodeDuplicateQuery):
14
14
  self,
15
15
  migration: SchemaMigration,
16
16
  **kwargs: Any,
17
- ):
17
+ ) -> None:
18
18
  new_node = SchemaNodeInfo(
19
19
  name=migration.new_schema.name,
20
20
  namespace=migration.new_schema.namespace,
@@ -10,6 +10,7 @@ from prefect.logging import get_run_logger
10
10
  from infrahub.core.branch import Branch # noqa: TC001
11
11
  from infrahub.core.constants import SYSTEM_USER_ID
12
12
  from infrahub.core.migrations import MIGRATION_MAP
13
+ from infrahub.core.migrations.shared import MigrationInput
13
14
  from infrahub.core.path import SchemaPath # noqa: TC001
14
15
  from infrahub.workers.dependencies import get_database
15
16
  from infrahub.workflows.utils import add_branch_tag
@@ -18,6 +19,7 @@ from .models import SchemaApplyMigrationData, SchemaMigrationPathResponseData
18
19
 
19
20
  if TYPE_CHECKING:
20
21
  from infrahub.core.schema import MainSchemaTypes
22
+ from infrahub.core.timestamp import Timestamp
21
23
  from infrahub.database import InfrahubDatabase
22
24
 
23
25
 
@@ -59,6 +61,7 @@ async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str
59
61
  schema_path=migration.path,
60
62
  database=await get_database(),
61
63
  user_id=message.user_id,
64
+ at=message.at,
62
65
  )
63
66
 
64
67
  async for _, result in batch.execute():
@@ -79,6 +82,7 @@ async def schema_path_migrate(
79
82
  migration_name: str,
80
83
  schema_path: SchemaPath,
81
84
  database: InfrahubDatabase,
85
+ at: Timestamp,
82
86
  new_node_schema: MainSchemaTypes | None = None,
83
87
  previous_node_schema: MainSchemaTypes | None = None,
84
88
  user_id: str = SYSTEM_USER_ID,
@@ -104,7 +108,9 @@ async def schema_path_migrate(
104
108
  previous_node_schema=previous_node_schema, # type: ignore[arg-type]
105
109
  schema_path=schema_path,
106
110
  )
107
- execution_result = await migration.execute(db=db, branch=branch, user_id=user_id)
111
+ execution_result = await migration.execute(
112
+ migration_input=MigrationInput(db=db, at=at, user_id=user_id), branch=branch
113
+ )
108
114
 
109
115
  log.info(f"Migration completed for {migration_name}")
110
116
  log.debug(f"execution_result {execution_result}")
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from dataclasses import dataclass, field
3
4
  from typing import TYPE_CHECKING, Any, Sequence
4
5
 
5
6
  from pydantic import BaseModel, ConfigDict, Field
@@ -46,6 +47,13 @@ class MigrationResult(BaseModel):
46
47
  return False
47
48
 
48
49
 
50
+ @dataclass
51
+ class MigrationInput:
52
+ db: InfrahubDatabase
53
+ at: Timestamp = field(default_factory=Timestamp)
54
+ user_id: str = SYSTEM_USER_ID
55
+
56
+
49
57
  class SchemaMigration(BaseModel):
50
58
  model_config = ConfigDict(arbitrary_types_allowed=True)
51
59
  name: str = Field(..., description="Name of the migration")
@@ -59,37 +67,37 @@ class SchemaMigration(BaseModel):
59
67
 
60
68
  async def execute_pre_queries(
61
69
  self,
62
- db: InfrahubDatabase, # noqa: ARG002
70
+ migration_input: MigrationInput, # noqa: ARG002
63
71
  result: MigrationResult,
64
72
  branch: Branch, # noqa: ARG002
65
- at: Timestamp, # noqa: ARG002
66
- user_id: str, # noqa: ARG002
67
73
  ) -> MigrationResult:
68
74
  return result
69
75
 
70
76
  async def execute_post_queries(
71
77
  self,
72
- db: InfrahubDatabase, # noqa: ARG002
78
+ migration_input: MigrationInput, # noqa: ARG002
73
79
  result: MigrationResult,
74
80
  branch: Branch, # noqa: ARG002
75
- at: Timestamp, # noqa: ARG002
76
- user_id: str, # noqa: ARG002
77
81
  ) -> MigrationResult:
78
82
  return result
79
83
 
80
84
  async def execute_queries(
81
85
  self,
82
- db: InfrahubDatabase,
86
+ migration_input: MigrationInput,
83
87
  result: MigrationResult,
84
88
  branch: Branch,
85
- at: Timestamp,
86
89
  queries: Sequence[type[MigrationBaseQuery]],
87
- user_id: str,
88
90
  ) -> MigrationResult:
89
91
  for migration_query in queries:
90
92
  try:
91
- query = await migration_query.init(db=db, branch=branch, at=at, migration=self, user_id=user_id)
92
- await query.execute(db=db)
93
+ query = await migration_query.init(
94
+ db=migration_input.db,
95
+ branch=branch,
96
+ at=migration_input.at,
97
+ migration=self,
98
+ user_id=migration_input.user_id,
99
+ )
100
+ await query.execute(db=migration_input.db)
93
101
  result.nbr_migrations_executed += query.get_nbr_migrations_executed()
94
102
  except Exception as exc:
95
103
  result.errors.append(str(exc))
@@ -99,22 +107,20 @@ class SchemaMigration(BaseModel):
99
107
 
100
108
  async def execute(
101
109
  self,
102
- db: InfrahubDatabase,
110
+ migration_input: MigrationInput,
103
111
  branch: Branch,
104
- at: Timestamp | str | None = None,
105
112
  queries: Sequence[type[MigrationBaseQuery]] | None = None,
106
- user_id: str = SYSTEM_USER_ID,
107
113
  ) -> MigrationResult:
108
- async with db.start_transaction() as ts:
114
+ async with migration_input.db.start_transaction() as ts:
109
115
  result = MigrationResult()
110
- at = Timestamp(at)
116
+ txn_migration_input = MigrationInput(db=ts, at=migration_input.at, user_id=migration_input.user_id)
111
117
 
112
- await self.execute_pre_queries(db=ts, result=result, branch=branch, at=at, user_id=user_id)
118
+ await self.execute_pre_queries(migration_input=txn_migration_input, result=result, branch=branch)
113
119
  queries_to_execute = queries or self.queries
114
120
  await self.execute_queries(
115
- db=ts, result=result, branch=branch, at=at, queries=queries_to_execute, user_id=user_id
121
+ migration_input=txn_migration_input, result=result, branch=branch, queries=queries_to_execute
116
122
  )
117
- await self.execute_post_queries(db=ts, result=result, branch=branch, at=at, user_id=user_id)
123
+ await self.execute_post_queries(migration_input=txn_migration_input, result=result, branch=branch)
118
124
 
119
125
  return result
120
126
 
@@ -174,16 +180,17 @@ class GraphMigration(BaseModel):
174
180
  async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult:
175
181
  raise NotImplementedError
176
182
 
177
- async def execute(self, db: InfrahubDatabase) -> MigrationResult:
178
- async with db.start_transaction() as ts:
179
- return await self.do_execute(db=ts)
183
+ async def execute(self, migration_input: MigrationInput) -> MigrationResult:
184
+ async with migration_input.db.start_transaction() as ts:
185
+ txn_migration_input = MigrationInput(db=ts, at=migration_input.at)
186
+ return await self.do_execute(migration_input=txn_migration_input)
180
187
 
181
- async def do_execute(self, db: InfrahubDatabase) -> MigrationResult:
188
+ async def do_execute(self, migration_input: MigrationInput) -> MigrationResult:
182
189
  result = MigrationResult()
183
190
  for migration_query in self.queries:
184
191
  try:
185
- query = await migration_query.init(db=db)
186
- await query.execute(db=db)
192
+ query = await migration_query.init(db=migration_input.db, at=migration_input.at)
193
+ await query.execute(db=migration_input.db)
187
194
  except Exception as exc:
188
195
  result.errors.append(str(exc))
189
196
  return result
@@ -216,14 +223,14 @@ class InternalSchemaMigration(BaseModel):
216
223
  async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult:
217
224
  raise NotImplementedError
218
225
 
219
- async def execute(self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID) -> MigrationResult:
226
+ async def execute(self, migration_input: MigrationInput) -> MigrationResult:
220
227
  result = MigrationResult()
221
228
 
222
229
  default_branch = registry.get_branch_from_registry()
223
230
 
224
231
  for migration in self.migrations:
225
232
  try:
226
- execution_result = await migration.execute(db=db, branch=default_branch, user_id=user_id)
233
+ execution_result = await migration.execute(migration_input=migration_input, branch=default_branch)
227
234
  result.errors.extend(execution_result.errors)
228
235
  except Exception as exc:
229
236
  result.errors.append(str(exc))
@@ -243,7 +250,7 @@ class ArbitraryMigration(BaseModel):
243
250
  async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult:
244
251
  raise NotImplementedError()
245
252
 
246
- async def execute(self, db: InfrahubDatabase) -> MigrationResult:
253
+ async def execute(self, migration_input: MigrationInput) -> MigrationResult:
247
254
  raise NotImplementedError()
248
255
 
249
256
 
@@ -259,11 +266,11 @@ class MigrationRequiringRebase(BaseModel):
259
266
  async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult:
260
267
  raise NotImplementedError()
261
268
 
262
- async def execute_against_branch(self, db: InfrahubDatabase, branch: Branch) -> MigrationResult:
269
+ async def execute_against_branch(self, migration_input: MigrationInput, branch: Branch) -> MigrationResult:
263
270
  """Method that will be run against non-default branches, it assumes that the branches have been rebased."""
264
271
  raise NotImplementedError()
265
272
 
266
- async def execute(self, db: InfrahubDatabase) -> MigrationResult:
273
+ async def execute(self, migration_input: MigrationInput) -> MigrationResult:
267
274
  """Method that will be run against the default branch."""
268
275
  raise NotImplementedError()
269
276
 
@@ -82,7 +82,7 @@ class Node(BaseNode, MetadataInterface, metaclass=BaseNodeMeta):
82
82
  _meta.default_filter = default_filter
83
83
  super().__init_subclass_with_meta__(_meta=_meta, **options)
84
84
 
85
- def __init__(self, schema: NodeSchema | ProfileSchema | TemplateSchema, branch: Branch, at: Timestamp):
85
+ def __init__(self, schema: NodeSchema | ProfileSchema | TemplateSchema, branch: Branch, at: Timestamp) -> None:
86
86
  super().__init__()
87
87
  self._schema: NodeSchema | ProfileSchema | TemplateSchema = schema
88
88
  self._branch: Branch = branch
@@ -408,6 +408,7 @@ class Node(BaseNode, MetadataInterface, metaclass=BaseNodeMeta):
408
408
  schema_node: NodeSchema | GenericSchema,
409
409
  schema_attribute: AttributeSchema,
410
410
  branch: Branch | None = None,
411
+ at: Timestamp | None = None,
411
412
  ) -> CoreNumberPool:
412
413
  """Fetch or create a number pool based on the schema attribute parameters.
413
414
 
@@ -456,7 +457,7 @@ class Node(BaseNode, MetadataInterface, metaclass=BaseNodeMeta):
456
457
  end_range=number_pool_parameters.end_range,
457
458
  pool_type=NumberPoolType.SCHEMA.value,
458
459
  )
459
- await number_pool.save(db=db)
460
+ await number_pool.save(db=db, at=at)
460
461
 
461
462
  # Do a lookup of the number pool to get the correct mapped type from the registry
462
463
  # without this we don't get access to the .get_resource() method.
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import Any
4
+
3
5
  from ..utils import SubclassWithMeta, SubclassWithMeta_Meta
4
6
 
5
7
 
@@ -9,19 +11,19 @@ class BaseOptions:
9
11
 
10
12
  _frozen: bool = False
11
13
 
12
- def __init__(self, class_type):
14
+ def __init__(self, class_type: type) -> None:
13
15
  self.class_type = class_type
14
16
 
15
17
  def freeze(self) -> None:
16
18
  self._frozen = True
17
19
 
18
- def __setattr__(self, name, value):
20
+ def __setattr__(self, name: str, value: Any) -> None:
19
21
  if not self._frozen:
20
22
  super().__setattr__(name, value)
21
23
  else:
22
24
  raise Exception(f"Can't modify frozen Options {self}")
23
25
 
24
- def __repr__(self):
26
+ def __repr__(self) -> str:
25
27
  return f"<{self.__class__.__name__} name={repr(self.name)}>"
26
28
 
27
29
 
@@ -34,7 +36,9 @@ class BaseNode(SubclassWithMeta):
34
36
  # return type(class_name, (cls,), {"Meta": options})
35
37
 
36
38
  @classmethod
37
- def __init_subclass_with_meta__(cls, name=None, description=None, _meta=None, **_kwargs) -> None:
39
+ def __init_subclass_with_meta__(
40
+ cls, name: str | None = None, description: str | None = None, _meta: BaseOptions | None = None, **_kwargs: Any
41
+ ) -> None:
38
42
  assert "_meta" not in cls.__dict__, "Can't assign meta directly"
39
43
  if not _meta:
40
44
  return
@@ -52,7 +56,7 @@ class BaseNodeOptions(BaseOptions):
52
56
 
53
57
 
54
58
  class ObjectNodeMeta(BaseNodeMeta):
55
- def __new__(mcs, name_, bases, namespace, **options):
59
+ def __new__(mcs, name_: str, bases: tuple[type, ...], namespace: dict[str, Any], **options: Any) -> type:
56
60
  # Note: it's safe to pass options as keyword arguments as they are still type-checked by NodeOptions.
57
61
 
58
62
  # We create this type, to then overload it with the dataclass attrs
@@ -117,7 +117,7 @@ class NodeDeleteIndex:
117
117
 
118
118
 
119
119
  class NodeDeleteValidator:
120
- def __init__(self, db: InfrahubDatabase, branch: Branch):
120
+ def __init__(self, db: InfrahubDatabase, branch: Branch) -> None:
121
121
  self.db = db
122
122
  self.branch = branch
123
123
  schema_branch = registry.schema.get_schema_branch(name=self.branch.name)