infrahub-server 1.2.12__py3-none-any.whl → 1.3.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 (205) hide show
  1. infrahub/actions/constants.py +130 -0
  2. infrahub/actions/gather.py +114 -0
  3. infrahub/actions/models.py +243 -0
  4. infrahub/actions/parsers.py +104 -0
  5. infrahub/actions/schema.py +393 -0
  6. infrahub/actions/tasks.py +119 -0
  7. infrahub/actions/triggers.py +21 -0
  8. infrahub/branch/__init__.py +0 -0
  9. infrahub/branch/tasks.py +29 -0
  10. infrahub/branch/triggers.py +22 -0
  11. infrahub/cli/db.py +3 -4
  12. infrahub/computed_attribute/gather.py +3 -1
  13. infrahub/computed_attribute/tasks.py +23 -29
  14. infrahub/core/account.py +24 -47
  15. infrahub/core/attribute.py +13 -15
  16. infrahub/core/constants/__init__.py +10 -0
  17. infrahub/core/constants/infrahubkind.py +9 -0
  18. infrahub/core/constraint/node/runner.py +3 -1
  19. infrahub/core/convert_object_type/__init__.py +0 -0
  20. infrahub/core/convert_object_type/conversion.py +124 -0
  21. infrahub/core/convert_object_type/schema_mapping.py +56 -0
  22. infrahub/core/diff/coordinator.py +8 -1
  23. infrahub/core/diff/query/all_conflicts.py +1 -5
  24. infrahub/core/diff/query/artifact.py +10 -20
  25. infrahub/core/diff/query/delete_query.py +8 -4
  26. infrahub/core/diff/query/diff_get.py +3 -6
  27. infrahub/core/diff/query/field_specifiers.py +1 -1
  28. infrahub/core/diff/query/field_summary.py +2 -4
  29. infrahub/core/diff/query/merge.py +72 -125
  30. infrahub/core/diff/query/save.py +28 -43
  31. infrahub/core/diff/query/summary_counts_enricher.py +34 -54
  32. infrahub/core/diff/query/time_range_query.py +0 -1
  33. infrahub/core/diff/repository/repository.py +4 -0
  34. infrahub/core/manager.py +14 -11
  35. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +1 -2
  36. infrahub/core/migrations/graph/m012_convert_account_generic.py +1 -1
  37. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +2 -6
  38. infrahub/core/migrations/graph/m015_diff_format_update.py +1 -2
  39. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -2
  40. infrahub/core/migrations/graph/m019_restore_rels_to_time.py +11 -22
  41. infrahub/core/migrations/graph/m020_duplicate_edges.py +3 -6
  42. infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +1 -2
  43. infrahub/core/migrations/graph/m023_deduplicate_cardinality_one_relationships.py +2 -2
  44. infrahub/core/migrations/graph/m024_missing_hierarchy_backfill.py +1 -2
  45. infrahub/core/migrations/graph/m028_delete_diffs.py +1 -2
  46. infrahub/core/migrations/graph/m029_duplicates_cleanup.py +30 -48
  47. infrahub/core/migrations/graph/m030_illegal_edges.py +1 -2
  48. infrahub/core/migrations/query/attribute_add.py +1 -2
  49. infrahub/core/migrations/query/attribute_rename.py +6 -11
  50. infrahub/core/migrations/query/delete_element_in_schema.py +19 -17
  51. infrahub/core/migrations/query/node_duplicate.py +19 -21
  52. infrahub/core/migrations/query/relationship_duplicate.py +19 -18
  53. infrahub/core/migrations/schema/node_attribute_remove.py +4 -8
  54. infrahub/core/migrations/schema/node_remove.py +19 -20
  55. infrahub/core/models.py +29 -2
  56. infrahub/core/node/__init__.py +131 -28
  57. infrahub/core/node/base.py +1 -1
  58. infrahub/core/node/create.py +211 -0
  59. infrahub/core/node/resource_manager/number_pool.py +31 -5
  60. infrahub/core/node/standard.py +6 -1
  61. infrahub/core/path.py +15 -1
  62. infrahub/core/protocols.py +57 -0
  63. infrahub/core/protocols_base.py +3 -0
  64. infrahub/core/query/__init__.py +2 -2
  65. infrahub/core/query/delete.py +3 -3
  66. infrahub/core/query/diff.py +19 -32
  67. infrahub/core/query/ipam.py +10 -20
  68. infrahub/core/query/node.py +29 -47
  69. infrahub/core/query/relationship.py +55 -34
  70. infrahub/core/query/resource_manager.py +1 -2
  71. infrahub/core/query/standard_node.py +19 -5
  72. infrahub/core/query/subquery.py +2 -4
  73. infrahub/core/relationship/constraints/count.py +10 -9
  74. infrahub/core/relationship/constraints/interface.py +2 -1
  75. infrahub/core/relationship/constraints/peer_kind.py +2 -1
  76. infrahub/core/relationship/constraints/peer_parent.py +56 -0
  77. infrahub/core/relationship/constraints/peer_relatives.py +72 -0
  78. infrahub/core/relationship/constraints/profiles_kind.py +1 -1
  79. infrahub/core/relationship/model.py +4 -1
  80. infrahub/core/schema/__init__.py +2 -1
  81. infrahub/core/schema/attribute_parameters.py +160 -0
  82. infrahub/core/schema/attribute_schema.py +130 -7
  83. infrahub/core/schema/basenode_schema.py +27 -3
  84. infrahub/core/schema/definitions/core/__init__.py +29 -1
  85. infrahub/core/schema/definitions/core/group.py +45 -0
  86. infrahub/core/schema/definitions/core/resource_pool.py +9 -0
  87. infrahub/core/schema/definitions/internal.py +43 -5
  88. infrahub/core/schema/generated/attribute_schema.py +16 -3
  89. infrahub/core/schema/generated/relationship_schema.py +11 -1
  90. infrahub/core/schema/manager.py +7 -2
  91. infrahub/core/schema/schema_branch.py +104 -9
  92. infrahub/core/validators/__init__.py +15 -2
  93. infrahub/core/validators/attribute/choices.py +1 -3
  94. infrahub/core/validators/attribute/enum.py +1 -3
  95. infrahub/core/validators/attribute/kind.py +1 -3
  96. infrahub/core/validators/attribute/length.py +13 -7
  97. infrahub/core/validators/attribute/min_max.py +118 -0
  98. infrahub/core/validators/attribute/number_pool.py +106 -0
  99. infrahub/core/validators/attribute/optional.py +1 -4
  100. infrahub/core/validators/attribute/regex.py +5 -6
  101. infrahub/core/validators/attribute/unique.py +1 -3
  102. infrahub/core/validators/determiner.py +18 -2
  103. infrahub/core/validators/enum.py +12 -0
  104. infrahub/core/validators/node/hierarchy.py +3 -6
  105. infrahub/core/validators/query.py +1 -3
  106. infrahub/core/validators/relationship/count.py +6 -12
  107. infrahub/core/validators/relationship/optional.py +2 -4
  108. infrahub/core/validators/relationship/peer.py +177 -12
  109. infrahub/core/validators/tasks.py +1 -1
  110. infrahub/core/validators/uniqueness/query.py +5 -9
  111. infrahub/database/__init__.py +12 -4
  112. infrahub/database/validation.py +1 -2
  113. infrahub/dependencies/builder/constraint/grouped/node_runner.py +4 -0
  114. infrahub/dependencies/builder/constraint/relationship_manager/peer_parent.py +8 -0
  115. infrahub/dependencies/builder/constraint/relationship_manager/peer_relatives.py +8 -0
  116. infrahub/dependencies/builder/constraint/schema/aggregated.py +2 -0
  117. infrahub/dependencies/builder/constraint/schema/relationship_peer.py +8 -0
  118. infrahub/dependencies/builder/diff/deserializer.py +1 -1
  119. infrahub/dependencies/registry.py +4 -0
  120. infrahub/events/group_action.py +1 -0
  121. infrahub/events/models.py +1 -1
  122. infrahub/git/base.py +5 -3
  123. infrahub/git/integrator.py +96 -5
  124. infrahub/git/tasks.py +1 -0
  125. infrahub/graphql/analyzer.py +139 -18
  126. infrahub/graphql/manager.py +4 -0
  127. infrahub/graphql/mutations/action.py +164 -0
  128. infrahub/graphql/mutations/convert_object_type.py +71 -0
  129. infrahub/graphql/mutations/main.py +24 -175
  130. infrahub/graphql/mutations/proposed_change.py +20 -17
  131. infrahub/graphql/mutations/relationship.py +32 -0
  132. infrahub/graphql/mutations/resource_manager.py +63 -7
  133. infrahub/graphql/queries/convert_object_type_mapping.py +34 -0
  134. infrahub/graphql/queries/resource_manager.py +7 -1
  135. infrahub/graphql/resolvers/many_relationship.py +1 -1
  136. infrahub/graphql/resolvers/resolver.py +2 -2
  137. infrahub/graphql/resolvers/single_relationship.py +1 -1
  138. infrahub/graphql/schema.py +6 -0
  139. infrahub/menu/menu.py +34 -2
  140. infrahub/message_bus/messages/__init__.py +0 -10
  141. infrahub/message_bus/operations/__init__.py +0 -8
  142. infrahub/message_bus/operations/refresh/registry.py +3 -6
  143. infrahub/patch/queries/delete_duplicated_edges.py +10 -15
  144. infrahub/pools/models.py +14 -0
  145. infrahub/pools/number.py +5 -3
  146. infrahub/pools/registration.py +22 -0
  147. infrahub/pools/tasks.py +126 -0
  148. infrahub/prefect_server/models.py +1 -19
  149. infrahub/proposed_change/models.py +68 -3
  150. infrahub/proposed_change/tasks.py +911 -34
  151. infrahub/schema/__init__.py +0 -0
  152. infrahub/schema/tasks.py +27 -0
  153. infrahub/schema/triggers.py +23 -0
  154. infrahub/task_manager/models.py +10 -6
  155. infrahub/trigger/catalogue.py +6 -0
  156. infrahub/trigger/models.py +23 -6
  157. infrahub/trigger/setup.py +26 -2
  158. infrahub/trigger/tasks.py +4 -2
  159. infrahub/types.py +6 -0
  160. infrahub/webhook/tasks.py +4 -8
  161. infrahub/workflows/catalogue.py +103 -1
  162. infrahub_sdk/client.py +43 -10
  163. infrahub_sdk/ctl/generator.py +4 -4
  164. infrahub_sdk/ctl/repository.py +1 -1
  165. infrahub_sdk/node/__init__.py +39 -0
  166. infrahub_sdk/node/attribute.py +122 -0
  167. infrahub_sdk/node/constants.py +21 -0
  168. infrahub_sdk/{node.py → node/node.py} +158 -803
  169. infrahub_sdk/node/parsers.py +15 -0
  170. infrahub_sdk/node/property.py +24 -0
  171. infrahub_sdk/node/related_node.py +266 -0
  172. infrahub_sdk/node/relationship.py +302 -0
  173. infrahub_sdk/protocols.py +112 -0
  174. infrahub_sdk/protocols_base.py +34 -2
  175. infrahub_sdk/pytest_plugin/items/python_transform.py +2 -1
  176. infrahub_sdk/query_groups.py +17 -5
  177. infrahub_sdk/schema/main.py +1 -0
  178. infrahub_sdk/schema/repository.py +16 -0
  179. infrahub_sdk/spec/object.py +1 -1
  180. infrahub_sdk/store.py +1 -1
  181. infrahub_sdk/testing/schemas/car_person.py +1 -0
  182. infrahub_sdk/utils.py +7 -20
  183. infrahub_sdk/yaml.py +6 -5
  184. {infrahub_server-1.2.12.dist-info → infrahub_server-1.3.0.dist-info}/METADATA +3 -3
  185. {infrahub_server-1.2.12.dist-info → infrahub_server-1.3.0.dist-info}/RECORD +192 -166
  186. infrahub_testcontainers/container.py +0 -1
  187. infrahub_testcontainers/docker-compose.test.yml +1 -1
  188. infrahub_testcontainers/helpers.py +8 -2
  189. infrahub/message_bus/messages/check_generator_run.py +0 -26
  190. infrahub/message_bus/messages/finalize_validator_execution.py +0 -15
  191. infrahub/message_bus/messages/proposed_change/base_with_diff.py +0 -16
  192. infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +0 -11
  193. infrahub/message_bus/messages/request_generatordefinition_check.py +0 -20
  194. infrahub/message_bus/messages/request_proposedchange_pipeline.py +0 -23
  195. infrahub/message_bus/operations/check/__init__.py +0 -3
  196. infrahub/message_bus/operations/check/generator.py +0 -156
  197. infrahub/message_bus/operations/finalize/__init__.py +0 -3
  198. infrahub/message_bus/operations/finalize/validator.py +0 -133
  199. infrahub/message_bus/operations/requests/__init__.py +0 -9
  200. infrahub/message_bus/operations/requests/generator_definition.py +0 -140
  201. infrahub/message_bus/operations/requests/proposed_change.py +0 -629
  202. /infrahub/{message_bus/messages/proposed_change → actions}/__init__.py +0 -0
  203. {infrahub_server-1.2.12.dist-info → infrahub_server-1.3.0.dist-info}/LICENSE.txt +0 -0
  204. {infrahub_server-1.2.12.dist-info → infrahub_server-1.3.0.dist-info}/WHEEL +0 -0
  205. {infrahub_server-1.2.12.dist-info → infrahub_server-1.3.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,130 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from typing import Self
5
+
6
+ from infrahub.core.constants import InfrahubKind
7
+ from infrahub.core.schema.dropdown import DropdownChoice
8
+ from infrahub.utils import InfrahubStringEnum
9
+
10
+
11
+ class NodeAction(InfrahubStringEnum):
12
+ CREATED = "created"
13
+ UPDATED = "updated"
14
+
15
+
16
+ class DropdownEnum(Enum):
17
+ @classmethod
18
+ def available_types(cls) -> list[DropdownChoice]:
19
+ return [cls.__members__[member].value for member in list(cls.__members__)]
20
+
21
+ @classmethod
22
+ def from_value(cls, value: str) -> Self:
23
+ for member in cls.__members__:
24
+ if value == cls.__members__[member].value.name:
25
+ return cls.__members__[member]
26
+
27
+ raise NotImplementedError(f"The defined value {value} doesn't match a value of {cls.__class__.__name__}")
28
+
29
+
30
+ class BranchScope(DropdownEnum):
31
+ ALL_BRANCHES = DropdownChoice(
32
+ name="all_branches",
33
+ label="All Branches",
34
+ description="All branches",
35
+ color="#4cd964",
36
+ )
37
+ DEFAULT_BRANCH = DropdownChoice(
38
+ name="default_branch",
39
+ label="Default Branch",
40
+ description="Only the default branch",
41
+ color="#5ac8fa",
42
+ )
43
+ OTHER_BRANCHES = DropdownChoice(
44
+ name="other_branches",
45
+ label="Other Branches",
46
+ description="All branches except the default branch",
47
+ color="#ff2d55",
48
+ )
49
+
50
+
51
+ class MemberAction(DropdownEnum):
52
+ ADD_MEMBER = DropdownChoice(
53
+ name="add_member",
54
+ label="Add member",
55
+ description="Add impacted member to the selected group",
56
+ color="#4cd964",
57
+ )
58
+ REMOVE_MEMBER = DropdownChoice(
59
+ name="remove_member",
60
+ label="Remove member",
61
+ description="Remove impacted member from the selected group",
62
+ color="#ff2d55",
63
+ )
64
+
65
+
66
+ class MemberUpdate(DropdownEnum):
67
+ ADDED = DropdownChoice(
68
+ name="added",
69
+ label="Added",
70
+ description="Trigger when members are added to this group",
71
+ color="#4cd964",
72
+ )
73
+ REMOVED = DropdownChoice(
74
+ name="removed",
75
+ label="Removed",
76
+ description="Trigger when members are removed from this group",
77
+ color="#ff2d55",
78
+ )
79
+
80
+
81
+ class RelationshipMatch(DropdownEnum):
82
+ ADDED = DropdownChoice(
83
+ name="added",
84
+ label="Added",
85
+ description="Check if the selected relationship was added",
86
+ color="#4cd964",
87
+ )
88
+ REMOVED = DropdownChoice(
89
+ name="removed",
90
+ label="Removed",
91
+ description="Check if the selected relationship was removed",
92
+ color="#ff2d55",
93
+ )
94
+ UPDATED = DropdownChoice(
95
+ name="updated",
96
+ label="Updated",
97
+ description="Check if the selected relationship was updated, added or removed.",
98
+ color="#5ac8fa",
99
+ )
100
+
101
+
102
+ class ValueMatch(DropdownEnum):
103
+ VALUE = DropdownChoice(
104
+ name="value",
105
+ label="Value",
106
+ description="Match against the current value",
107
+ color="#4cd964",
108
+ )
109
+ VALUE_PREVIOUS = DropdownChoice(
110
+ name="value_previous",
111
+ label="Value Previous",
112
+ description="Match against the previous value",
113
+ color="#ff2d55",
114
+ )
115
+ VALUE_FULL = DropdownChoice(
116
+ name="value_full",
117
+ label="Full value match",
118
+ description="Match against both the current and previous values",
119
+ color="#5ac8fa",
120
+ )
121
+
122
+
123
+ NODES_THAT_TRIGGER_ACTION_RULES_SETUP = [
124
+ InfrahubKind.GENERATORACTION,
125
+ InfrahubKind.GROUPACTION,
126
+ InfrahubKind.GROUPTRIGGERRULE,
127
+ InfrahubKind.NODETRIGGERRULE,
128
+ InfrahubKind.NODETRIGGERATTRIBUTEMATCH,
129
+ InfrahubKind.NODETRIGGERRELATIONSHIPMATCH,
130
+ ]
@@ -0,0 +1,114 @@
1
+ from __future__ import annotations
2
+
3
+ from graphql import graphql
4
+ from infrahub_sdk.graphql import Query
5
+ from prefect import task
6
+ from prefect.cache_policies import NONE
7
+
8
+ from infrahub.core.constants import InfrahubKind
9
+ from infrahub.core.registry import registry
10
+ from infrahub.database import InfrahubDatabase # noqa: TC001 needed for prefect flow
11
+ from infrahub.graphql.initialization import prepare_graphql_params
12
+
13
+ from .models import ActionTriggerRuleTriggerDefinition
14
+ from .parsers import parse_trigger_rule_response
15
+
16
+
17
+ @task(
18
+ name="gather-trigger-action-rules",
19
+ cache_policy=NONE,
20
+ )
21
+ async def gather_trigger_action_rules(db: InfrahubDatabase) -> list[ActionTriggerRuleTriggerDefinition]:
22
+ trigger_query = Query(
23
+ name=InfrahubKind.TRIGGERRULE,
24
+ query={
25
+ InfrahubKind.TRIGGERRULE: {
26
+ "edges": {
27
+ "node": {
28
+ "__typename": None,
29
+ "id": None,
30
+ "name": {"value": None},
31
+ "branch_scope": {"value": None},
32
+ "active": {"value": None},
33
+ "... on CoreNodeTriggerRule": {
34
+ "node_kind": {"value": None},
35
+ "mutation_action": {"value": None},
36
+ "matches": {
37
+ "edges": {
38
+ "node": {
39
+ "__typename": None,
40
+ "id": None,
41
+ "... on CoreNodeTriggerAttributeMatch": {
42
+ "attribute_name": {"value": None},
43
+ "value": {"value": None},
44
+ "value_previous": {"value": None},
45
+ "value_match": {"value": None},
46
+ },
47
+ "... on CoreNodeTriggerRelationshipMatch": {
48
+ "relationship_name": {"value": None},
49
+ "modification_type": {"value": None},
50
+ "peer": {"value": None},
51
+ },
52
+ }
53
+ }
54
+ },
55
+ },
56
+ "... on CoreGroupTriggerRule": {
57
+ "member_update": {"value": None},
58
+ "group": {
59
+ "node": {
60
+ "id": None,
61
+ "__typename": None,
62
+ }
63
+ },
64
+ },
65
+ "action": {
66
+ "node": {
67
+ "__typename": None,
68
+ "id": None,
69
+ "name": {"value": None},
70
+ "... on CoreGroupAction": {
71
+ "member_action": {"value": None},
72
+ "group": {
73
+ "node": {
74
+ "id": None,
75
+ "__typename": None,
76
+ }
77
+ },
78
+ },
79
+ "... on CoreGeneratorAction": {
80
+ "generator": {
81
+ "node": {
82
+ "__typename": None,
83
+ "id": None,
84
+ }
85
+ },
86
+ },
87
+ }
88
+ },
89
+ }
90
+ },
91
+ }
92
+ },
93
+ )
94
+ gql_params = await prepare_graphql_params(
95
+ db=db,
96
+ branch=registry.default_branch,
97
+ )
98
+ response = await graphql(
99
+ schema=gql_params.schema,
100
+ source=trigger_query.render(),
101
+ context_value=gql_params.context,
102
+ root_value=None,
103
+ variable_values={},
104
+ )
105
+
106
+ data = response.data or {}
107
+ trigger_rules = parse_trigger_rule_response(data)
108
+ trigger_candidates = [
109
+ ActionTriggerRuleTriggerDefinition.from_trigger_rule(trigger_rule=trigger_rule)
110
+ for trigger_rule in trigger_rules
111
+ if trigger_rule.active
112
+ ]
113
+
114
+ return [trigger_rule for trigger_rule in trigger_candidates if trigger_rule]
@@ -0,0 +1,243 @@
1
+ from typing import Self
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from infrahub.core.registry import registry
6
+ from infrahub.events import (
7
+ GroupMemberAddedEvent,
8
+ GroupMemberRemovedEvent,
9
+ NodeCreatedEvent,
10
+ NodeDeletedEvent,
11
+ NodeUpdatedEvent,
12
+ )
13
+ from infrahub.trigger.models import (
14
+ EventTrigger,
15
+ ExecuteWorkflow,
16
+ TriggerDefinition,
17
+ TriggerType,
18
+ )
19
+ from infrahub.workflows.catalogue import (
20
+ ACTION_ADD_NODE_TO_GROUP,
21
+ ACTION_RUN_GENERATOR,
22
+ ACTION_RUN_GENERATOR_GROUP_EVENT,
23
+ REMOVE_ADD_NODE_FROM_GROUP,
24
+ )
25
+
26
+ from .constants import BranchScope, MemberAction, MemberUpdate, RelationshipMatch, ValueMatch
27
+
28
+
29
+ class EventGroupMember(BaseModel):
30
+ id: str
31
+ kind: str
32
+
33
+
34
+ class CoreAction(BaseModel):
35
+ """CoreAction generic"""
36
+
37
+
38
+ class CoreGeneratorAction(CoreAction):
39
+ generator_id: str
40
+
41
+
42
+ class CoreGroupAction(CoreAction):
43
+ member_action: MemberAction
44
+ group_id: str
45
+
46
+
47
+ class CoreTriggerRule(BaseModel):
48
+ name: str
49
+ branch_scope: BranchScope
50
+ action: CoreAction
51
+ active: bool
52
+
53
+
54
+ class CoreGroupTriggerRule(CoreTriggerRule):
55
+ member_update: MemberUpdate
56
+ group_id: str
57
+ group_kind: str
58
+
59
+
60
+ class CoreNodeTriggerMatch(BaseModel):
61
+ """Node Trigger Match Generic"""
62
+
63
+
64
+ class CoreNodeTriggerAttributeMatch(CoreNodeTriggerMatch):
65
+ attribute_name: str
66
+ value: str | None
67
+ value_previous: str | None
68
+ value_match: ValueMatch
69
+
70
+
71
+ class CoreNodeTriggerRelationshipMatch(CoreNodeTriggerMatch):
72
+ relationship_name: str
73
+ modification_type: RelationshipMatch
74
+ peer: str | None
75
+
76
+
77
+ class CoreNodeTriggerRule(CoreTriggerRule):
78
+ node_kind: str
79
+ mutation_action: str
80
+ matches: list[CoreNodeTriggerMatch]
81
+
82
+
83
+ class ActionTriggerRuleTriggerDefinition(TriggerDefinition):
84
+ type: TriggerType = TriggerType.ACTION_TRIGGER_RULE
85
+
86
+ @classmethod
87
+ def from_trigger_rule(cls, trigger_rule: CoreTriggerRule) -> Self | None:
88
+ if isinstance(trigger_rule, CoreNodeTriggerRule):
89
+ return cls._from_node_trigger(trigger_rule=trigger_rule)
90
+
91
+ if isinstance(trigger_rule, CoreGroupTriggerRule):
92
+ return cls._from_group_trigger(trigger_rule=trigger_rule)
93
+
94
+ return None
95
+
96
+ @classmethod
97
+ def _from_node_trigger(
98
+ cls,
99
+ trigger_rule: CoreNodeTriggerRule,
100
+ ) -> Self:
101
+ event_trigger = EventTrigger()
102
+
103
+ match trigger_rule.mutation_action:
104
+ case "created":
105
+ event_trigger.events.add(NodeCreatedEvent.event_name)
106
+ case "deleted":
107
+ event_trigger.events.add(NodeDeletedEvent.event_name)
108
+ case "updated":
109
+ event_trigger.events.add(NodeUpdatedEvent.event_name)
110
+
111
+ event_trigger.match = {"infrahub.node.kind": trigger_rule.node_kind}
112
+
113
+ match trigger_rule.branch_scope:
114
+ case BranchScope.DEFAULT_BRANCH:
115
+ event_trigger.match["infrahub.branch.name"] = registry.default_branch
116
+ case BranchScope.OTHER_BRANCHES:
117
+ event_trigger.match["infrahub.branch.name"] = f"!{registry.default_branch}"
118
+
119
+ related_matches: list[dict[str, str | list[str]]] = []
120
+ for match in trigger_rule.matches:
121
+ if isinstance(match, CoreNodeTriggerAttributeMatch):
122
+ match_related: dict[str, str | list[str]] = {
123
+ "prefect.resource.role": "infrahub.node.attribute_update",
124
+ "infrahub.field.name": match.attribute_name,
125
+ "infrahub.attribute.action": ["added", "updated", "removed"],
126
+ }
127
+
128
+ match match.value_match:
129
+ case ValueMatch.VALUE:
130
+ match_related["infrahub.attribute.value"] = match.value or ""
131
+ case ValueMatch.VALUE_PREVIOUS:
132
+ match_related["infrahub.attribute.value_previous"] = match.value_previous or ""
133
+ case ValueMatch.VALUE_FULL:
134
+ match_related["infrahub.attribute.value"] = match.value or ""
135
+ match_related["infrahub.attribute.value_previous"] = match.value_previous or ""
136
+
137
+ elif isinstance(match, CoreNodeTriggerRelationshipMatch):
138
+ match_related = {
139
+ "prefect.resource.role": "infrahub.node.relationship_update",
140
+ "infrahub.field.name": match.relationship_name,
141
+ }
142
+ if isinstance(match.peer, str):
143
+ match_related["infrahub.relationship.peer_id"] = match.peer
144
+
145
+ if match.modification_type != RelationshipMatch.UPDATED:
146
+ match_related["infrahub.relationship.peer_status"] = match.modification_type.value.name
147
+
148
+ related_matches.append(match_related)
149
+
150
+ event_trigger.match_related = related_matches or {}
151
+
152
+ if isinstance(trigger_rule.action, CoreGeneratorAction):
153
+ workflow = ExecuteWorkflow(
154
+ workflow=ACTION_RUN_GENERATOR,
155
+ parameters={
156
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
157
+ "generator_definition_id": trigger_rule.action.generator_id,
158
+ "node_ids": ["{{ event.resource['infrahub.node.id'] }}"],
159
+ "context": {
160
+ "__prefect_kind": "json",
161
+ "value": {
162
+ "__prefect_kind": "jinja",
163
+ "template": "{{ event.payload['context'] | tojson }}",
164
+ },
165
+ },
166
+ },
167
+ )
168
+ elif isinstance(trigger_rule.action, CoreGroupAction):
169
+ if trigger_rule.action.member_action == MemberAction.ADD_MEMBER:
170
+ flow = ACTION_ADD_NODE_TO_GROUP
171
+ else:
172
+ flow = REMOVE_ADD_NODE_FROM_GROUP
173
+
174
+ workflow = ExecuteWorkflow(
175
+ workflow=flow,
176
+ parameters={
177
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
178
+ "group_id": trigger_rule.action.group_id,
179
+ "node_id": "{{ event.resource['infrahub.node.id'] }}",
180
+ "context": {
181
+ "__prefect_kind": "json",
182
+ "value": {
183
+ "__prefect_kind": "jinja",
184
+ "template": "{{ event.payload['context'] | tojson }}",
185
+ },
186
+ },
187
+ },
188
+ )
189
+
190
+ return cls(
191
+ name=trigger_rule.name,
192
+ trigger=event_trigger,
193
+ actions=[workflow],
194
+ )
195
+
196
+ @classmethod
197
+ def _from_group_trigger(
198
+ cls,
199
+ trigger_rule: CoreGroupTriggerRule,
200
+ ) -> Self:
201
+ event_trigger = EventTrigger()
202
+
203
+ if trigger_rule.member_update == MemberUpdate.ADDED:
204
+ event_trigger.events.add(GroupMemberAddedEvent.event_name)
205
+ else:
206
+ event_trigger.events.add(GroupMemberRemovedEvent.event_name)
207
+
208
+ event_trigger.match = {"infrahub.node.kind": trigger_rule.group_kind, "infrahub.node.id": trigger_rule.group_id}
209
+
210
+ match trigger_rule.branch_scope:
211
+ case BranchScope.DEFAULT_BRANCH:
212
+ event_trigger.match["infrahub.branch.name"] = registry.default_branch
213
+ case BranchScope.OTHER_BRANCHES:
214
+ event_trigger.match["infrahub.branch.name"] = f"!{registry.default_branch}"
215
+
216
+ if isinstance(trigger_rule.action, CoreGeneratorAction):
217
+ workflow = ExecuteWorkflow(
218
+ workflow=ACTION_RUN_GENERATOR_GROUP_EVENT,
219
+ parameters={
220
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
221
+ "generator_definition_id": trigger_rule.action.generator_id,
222
+ "members": {
223
+ "__prefect_kind": "json",
224
+ "value": {
225
+ "__prefect_kind": "jinja",
226
+ "template": "{{ event.payload['data']['members']| tojson }}",
227
+ },
228
+ },
229
+ "context": {
230
+ "__prefect_kind": "json",
231
+ "value": {
232
+ "__prefect_kind": "jinja",
233
+ "template": "{{ event.payload['context'] | tojson }}",
234
+ },
235
+ },
236
+ },
237
+ )
238
+
239
+ return cls(
240
+ name=trigger_rule.name,
241
+ trigger=event_trigger,
242
+ actions=[workflow],
243
+ )
@@ -0,0 +1,104 @@
1
+ from typing import Any
2
+
3
+ from infrahub.core.constants import InfrahubKind
4
+
5
+ from .constants import BranchScope, MemberAction, MemberUpdate, RelationshipMatch, ValueMatch
6
+ from .models import (
7
+ CoreAction,
8
+ CoreGeneratorAction,
9
+ CoreGroupAction,
10
+ CoreGroupTriggerRule,
11
+ CoreNodeTriggerAttributeMatch,
12
+ CoreNodeTriggerMatch,
13
+ CoreNodeTriggerRelationshipMatch,
14
+ CoreNodeTriggerRule,
15
+ CoreTriggerRule,
16
+ )
17
+
18
+
19
+ def parse_trigger_rule_response(data: dict[str, Any]) -> list[CoreTriggerRule]:
20
+ rules: list[CoreTriggerRule] = []
21
+ if kind := data.get(InfrahubKind.TRIGGERRULE):
22
+ if edges := kind.get("edges"):
23
+ for edge in edges:
24
+ if rule := _parse_graphql_node(edge["node"]):
25
+ rules.append(rule)
26
+
27
+ return rules
28
+
29
+
30
+ def _parse_graphql_node(data: dict[str, Any]) -> CoreTriggerRule | None:
31
+ typename = data.get("__typename")
32
+ name = data["name"]["value"]
33
+ active = data["active"]["value"]
34
+ branch_scope = BranchScope.from_value(value=data["branch_scope"]["value"])
35
+ action = _parse_graphql_action_response(data=data["action"]["node"])
36
+ match typename:
37
+ case "CoreGroupTriggerRule":
38
+ member_update = MemberUpdate.from_value(data["member_update"]["value"])
39
+ group_id = data["group"]["node"]["id"]
40
+ group_kind = data["group"]["node"]["__typename"]
41
+ return CoreGroupTriggerRule(
42
+ name=name,
43
+ branch_scope=branch_scope,
44
+ action=action,
45
+ member_update=member_update,
46
+ group_id=group_id,
47
+ group_kind=group_kind,
48
+ active=active,
49
+ )
50
+ case "CoreNodeTriggerRule":
51
+ node_kind = data["node_kind"]["value"]
52
+ mutation_action = data["mutation_action"]["value"]
53
+ matches = _parse_node_trigger_matches(data=data["matches"]["edges"])
54
+ return CoreNodeTriggerRule(
55
+ name=name,
56
+ branch_scope=branch_scope,
57
+ action=action,
58
+ node_kind=node_kind,
59
+ mutation_action=mutation_action,
60
+ matches=matches,
61
+ active=active,
62
+ )
63
+ return None
64
+
65
+
66
+ def _parse_graphql_action_response(data: dict[str, Any]) -> CoreAction:
67
+ typename = data["__typename"]
68
+ match typename:
69
+ case "CoreGeneratorAction":
70
+ generator_id = data["generator"]["node"]["id"]
71
+ return CoreGeneratorAction(generator_id=generator_id)
72
+ case "CoreGroupAction":
73
+ member_action = MemberAction.from_value(data["member_action"]["value"])
74
+ group_id = data["group"]["node"]["id"]
75
+ return CoreGroupAction(member_action=member_action, group_id=group_id)
76
+
77
+ raise NotImplementedError(f"{typename} is not a valid CoreAction")
78
+
79
+
80
+ def _parse_node_trigger_matches(data: list[dict[str, Any]]) -> list[CoreNodeTriggerMatch]:
81
+ matches: list[CoreNodeTriggerMatch] = []
82
+ for entry in data:
83
+ node = entry["node"]
84
+ typename = node["__typename"]
85
+ match typename:
86
+ case "CoreNodeTriggerAttributeMatch":
87
+ matches.append(
88
+ CoreNodeTriggerAttributeMatch(
89
+ attribute_name=node["attribute_name"]["value"],
90
+ value=node["value"]["value"],
91
+ value_previous=node["value_previous"]["value"],
92
+ value_match=ValueMatch.from_value(value=node["value_match"]["value"]),
93
+ )
94
+ )
95
+ case "CoreNodeTriggerRelationshipMatch":
96
+ matches.append(
97
+ CoreNodeTriggerRelationshipMatch(
98
+ relationship_name=node["relationship_name"]["value"],
99
+ modification_type=RelationshipMatch.from_value(node["modification_type"]["value"]),
100
+ peer=node["peer"]["value"],
101
+ )
102
+ )
103
+
104
+ return matches