infrahub-server 1.2.11__py3-none-any.whl → 1.3.0b1__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 (147) hide show
  1. infrahub/actions/constants.py +86 -0
  2. infrahub/actions/gather.py +114 -0
  3. infrahub/actions/models.py +241 -0
  4. infrahub/actions/parsers.py +104 -0
  5. infrahub/actions/schema.py +382 -0
  6. infrahub/actions/tasks.py +126 -0
  7. infrahub/actions/triggers.py +21 -0
  8. infrahub/cli/db.py +1 -2
  9. infrahub/core/account.py +24 -47
  10. infrahub/core/attribute.py +13 -15
  11. infrahub/core/constants/__init__.py +5 -0
  12. infrahub/core/constants/infrahubkind.py +9 -0
  13. infrahub/core/convert_object_type/__init__.py +0 -0
  14. infrahub/core/convert_object_type/conversion.py +122 -0
  15. infrahub/core/convert_object_type/schema_mapping.py +56 -0
  16. infrahub/core/diff/query/all_conflicts.py +1 -5
  17. infrahub/core/diff/query/artifact.py +10 -20
  18. infrahub/core/diff/query/diff_get.py +3 -6
  19. infrahub/core/diff/query/field_summary.py +2 -4
  20. infrahub/core/diff/query/merge.py +70 -123
  21. infrahub/core/diff/query/save.py +20 -32
  22. infrahub/core/diff/query/summary_counts_enricher.py +34 -54
  23. infrahub/core/manager.py +14 -11
  24. infrahub/core/migrations/graph/m003_relationship_parent_optional.py +1 -2
  25. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +2 -4
  26. infrahub/core/migrations/graph/m019_restore_rels_to_time.py +11 -22
  27. infrahub/core/migrations/graph/m020_duplicate_edges.py +3 -6
  28. infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +1 -2
  29. infrahub/core/migrations/graph/m024_missing_hierarchy_backfill.py +1 -2
  30. infrahub/core/migrations/query/attribute_add.py +1 -2
  31. infrahub/core/migrations/query/attribute_rename.py +5 -10
  32. infrahub/core/migrations/query/delete_element_in_schema.py +19 -17
  33. infrahub/core/migrations/query/node_duplicate.py +19 -21
  34. infrahub/core/migrations/query/relationship_duplicate.py +19 -17
  35. infrahub/core/migrations/schema/node_attribute_remove.py +4 -8
  36. infrahub/core/migrations/schema/node_remove.py +19 -19
  37. infrahub/core/models.py +29 -2
  38. infrahub/core/node/__init__.py +90 -18
  39. infrahub/core/node/create.py +211 -0
  40. infrahub/core/node/resource_manager/number_pool.py +31 -5
  41. infrahub/core/node/standard.py +6 -1
  42. infrahub/core/protocols.py +56 -0
  43. infrahub/core/protocols_base.py +3 -0
  44. infrahub/core/query/__init__.py +2 -2
  45. infrahub/core/query/diff.py +19 -32
  46. infrahub/core/query/ipam.py +10 -20
  47. infrahub/core/query/node.py +28 -46
  48. infrahub/core/query/relationship.py +53 -32
  49. infrahub/core/query/resource_manager.py +1 -2
  50. infrahub/core/query/subquery.py +2 -4
  51. infrahub/core/relationship/model.py +3 -0
  52. infrahub/core/schema/__init__.py +2 -1
  53. infrahub/core/schema/attribute_parameters.py +160 -0
  54. infrahub/core/schema/attribute_schema.py +111 -8
  55. infrahub/core/schema/basenode_schema.py +25 -1
  56. infrahub/core/schema/definitions/core/__init__.py +29 -1
  57. infrahub/core/schema/definitions/core/group.py +45 -0
  58. infrahub/core/schema/definitions/internal.py +27 -4
  59. infrahub/core/schema/generated/attribute_schema.py +16 -3
  60. infrahub/core/schema/manager.py +3 -0
  61. infrahub/core/schema/schema_branch.py +67 -7
  62. infrahub/core/validators/__init__.py +13 -1
  63. infrahub/core/validators/attribute/choices.py +1 -3
  64. infrahub/core/validators/attribute/enum.py +1 -3
  65. infrahub/core/validators/attribute/kind.py +1 -3
  66. infrahub/core/validators/attribute/length.py +13 -7
  67. infrahub/core/validators/attribute/min_max.py +118 -0
  68. infrahub/core/validators/attribute/number_pool.py +106 -0
  69. infrahub/core/validators/attribute/optional.py +1 -4
  70. infrahub/core/validators/attribute/regex.py +5 -6
  71. infrahub/core/validators/attribute/unique.py +1 -3
  72. infrahub/core/validators/determiner.py +18 -2
  73. infrahub/core/validators/enum.py +12 -0
  74. infrahub/core/validators/node/hierarchy.py +3 -6
  75. infrahub/core/validators/query.py +1 -3
  76. infrahub/core/validators/relationship/count.py +6 -12
  77. infrahub/core/validators/relationship/optional.py +2 -4
  78. infrahub/core/validators/relationship/peer.py +3 -8
  79. infrahub/core/validators/uniqueness/query.py +5 -9
  80. infrahub/database/__init__.py +11 -2
  81. infrahub/events/group_action.py +1 -0
  82. infrahub/git/base.py +5 -3
  83. infrahub/git/integrator.py +102 -3
  84. infrahub/graphql/analyzer.py +139 -18
  85. infrahub/graphql/manager.py +4 -0
  86. infrahub/graphql/mutations/action.py +164 -0
  87. infrahub/graphql/mutations/convert_object_type.py +62 -0
  88. infrahub/graphql/mutations/main.py +24 -175
  89. infrahub/graphql/mutations/proposed_change.py +20 -17
  90. infrahub/graphql/mutations/resource_manager.py +62 -6
  91. infrahub/graphql/queries/convert_object_type_mapping.py +36 -0
  92. infrahub/graphql/queries/resource_manager.py +7 -1
  93. infrahub/graphql/schema.py +6 -0
  94. infrahub/menu/menu.py +31 -0
  95. infrahub/message_bus/messages/__init__.py +0 -10
  96. infrahub/message_bus/operations/__init__.py +0 -8
  97. infrahub/patch/queries/consolidate_duplicated_nodes.py +3 -6
  98. infrahub/patch/queries/delete_duplicated_edges.py +5 -10
  99. infrahub/pools/number.py +5 -3
  100. infrahub/prefect_server/models.py +1 -19
  101. infrahub/proposed_change/models.py +68 -3
  102. infrahub/proposed_change/tasks.py +907 -30
  103. infrahub/task_manager/models.py +10 -6
  104. infrahub/trigger/catalogue.py +2 -0
  105. infrahub/trigger/models.py +18 -2
  106. infrahub/trigger/tasks.py +3 -1
  107. infrahub/types.py +6 -0
  108. infrahub/workflows/catalogue.py +76 -0
  109. infrahub_sdk/client.py +43 -10
  110. infrahub_sdk/node/__init__.py +39 -0
  111. infrahub_sdk/node/attribute.py +122 -0
  112. infrahub_sdk/node/constants.py +21 -0
  113. infrahub_sdk/{node.py → node/node.py} +50 -749
  114. infrahub_sdk/node/parsers.py +15 -0
  115. infrahub_sdk/node/property.py +24 -0
  116. infrahub_sdk/node/related_node.py +266 -0
  117. infrahub_sdk/node/relationship.py +302 -0
  118. infrahub_sdk/protocols.py +112 -0
  119. infrahub_sdk/protocols_base.py +34 -2
  120. infrahub_sdk/query_groups.py +13 -2
  121. infrahub_sdk/schema/main.py +1 -0
  122. infrahub_sdk/schema/repository.py +16 -0
  123. infrahub_sdk/spec/object.py +1 -1
  124. infrahub_sdk/store.py +1 -1
  125. infrahub_sdk/testing/schemas/car_person.py +1 -0
  126. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/METADATA +4 -4
  127. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/RECORD +134 -122
  128. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/WHEEL +1 -1
  129. infrahub_testcontainers/container.py +0 -1
  130. infrahub_testcontainers/docker-compose.test.yml +1 -1
  131. infrahub_testcontainers/helpers.py +8 -2
  132. infrahub/message_bus/messages/check_generator_run.py +0 -26
  133. infrahub/message_bus/messages/finalize_validator_execution.py +0 -15
  134. infrahub/message_bus/messages/proposed_change/base_with_diff.py +0 -16
  135. infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +0 -11
  136. infrahub/message_bus/messages/request_generatordefinition_check.py +0 -20
  137. infrahub/message_bus/messages/request_proposedchange_pipeline.py +0 -23
  138. infrahub/message_bus/operations/check/__init__.py +0 -3
  139. infrahub/message_bus/operations/check/generator.py +0 -156
  140. infrahub/message_bus/operations/finalize/__init__.py +0 -3
  141. infrahub/message_bus/operations/finalize/validator.py +0 -133
  142. infrahub/message_bus/operations/requests/__init__.py +0 -9
  143. infrahub/message_bus/operations/requests/generator_definition.py +0 -140
  144. infrahub/message_bus/operations/requests/proposed_change.py +0 -629
  145. /infrahub/{message_bus/messages/proposed_change → actions}/__init__.py +0 -0
  146. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/LICENSE.txt +0 -0
  147. {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+
5
+ from infrahub.core.constants import InfrahubKind
6
+ from infrahub.core.schema.dropdown import DropdownChoice
7
+ from infrahub.utils import InfrahubStringEnum
8
+
9
+
10
+ class NodeAction(InfrahubStringEnum):
11
+ CREATED = "created"
12
+ DELETED = "deleted"
13
+ UPDATED = "updated"
14
+
15
+
16
+ class BranchScope(Enum):
17
+ ALL_BRANCHES = DropdownChoice(
18
+ name="all_branches",
19
+ label="All Branches",
20
+ description="All branches",
21
+ color="#fef08a",
22
+ )
23
+ DEFAULT_BRANCH = DropdownChoice(
24
+ name="default_branch",
25
+ label="Default Branch",
26
+ description="Only the default branch",
27
+ color="#86efac",
28
+ )
29
+ OTHER_BRANCHES = DropdownChoice(
30
+ name="other_branches",
31
+ label="Other Branches",
32
+ description="All branches except the default branch",
33
+ color="#e5e7eb",
34
+ )
35
+
36
+ @classmethod
37
+ def available_types(cls) -> list[DropdownChoice]:
38
+ return [cls.__members__[member].value for member in list(cls.__members__)]
39
+
40
+ @classmethod
41
+ def from_value(cls, value: str) -> BranchScope:
42
+ for member in cls.__members__:
43
+ if value == cls.__members__[member].value.name:
44
+ return cls.__members__[member]
45
+
46
+ raise NotImplementedError(f"The defined value {value} doesn't match a branch scope")
47
+
48
+
49
+ class ValueMatch(Enum):
50
+ VALUE = DropdownChoice(
51
+ name="value",
52
+ label="Value",
53
+ description="Match against the current value",
54
+ color="#fef08a",
55
+ )
56
+ VALUE_PREVIOUS = DropdownChoice(
57
+ name="value_previous",
58
+ label="Value Previous",
59
+ description="Match against the previous value",
60
+ color="#86efac",
61
+ )
62
+ VALUE_FULL = DropdownChoice(
63
+ name="value_full",
64
+ label="Full value match",
65
+ description="Match against both the current and previous values",
66
+ color="#e5e7eb",
67
+ )
68
+
69
+ @classmethod
70
+ def available_types(cls) -> list[DropdownChoice]:
71
+ return [cls.__members__[member].value for member in list(cls.__members__)]
72
+
73
+ @classmethod
74
+ def from_value(cls, value: str) -> ValueMatch:
75
+ for member in cls.__members__:
76
+ if value == cls.__members__[member].value.name:
77
+ return cls.__members__[member]
78
+
79
+ raise NotImplementedError(f"The defined value {value} doesn't match a ValueMatch")
80
+
81
+
82
+ NODES_THAT_TRIGGER_ACTION_RULES_SETUP = [
83
+ InfrahubKind.GROUPACTION,
84
+ InfrahubKind.GROUPTRIGGERRULE,
85
+ InfrahubKind.NODETRIGGERRULE,
86
+ ]
@@ -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
+ "added": {"value": None},
50
+ "peer": {"value": None},
51
+ },
52
+ }
53
+ }
54
+ },
55
+ },
56
+ "... on CoreGroupTriggerRule": {
57
+ "members_added": {"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
+ "add_members": {"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,241 @@
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, 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
+ add_members: bool
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
+ members_added: bool
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
+ added: bool
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
+ peer_status = "added" if match.added else "removed"
139
+ match_related = {
140
+ "prefect.resource.role": "infrahub.node.relationship_update",
141
+ "infrahub.field.name": match.relationship_name,
142
+ "infrahub.relationship.peer_status": peer_status,
143
+ }
144
+ if isinstance(match.peer, str):
145
+ match_related["infrahub.relationship.peer_id"] = match.peer
146
+ related_matches.append(match_related)
147
+
148
+ event_trigger.match_related = related_matches or {}
149
+
150
+ if isinstance(trigger_rule.action, CoreGeneratorAction):
151
+ workflow = ExecuteWorkflow(
152
+ workflow=ACTION_RUN_GENERATOR,
153
+ parameters={
154
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
155
+ "generator_definition_id": trigger_rule.action.generator_id,
156
+ "node_ids": ["{{ event.resource['infrahub.node.id'] }}"],
157
+ "context": {
158
+ "__prefect_kind": "json",
159
+ "value": {
160
+ "__prefect_kind": "jinja",
161
+ "template": "{{ event.payload['context'] | tojson }}",
162
+ },
163
+ },
164
+ },
165
+ )
166
+ elif isinstance(trigger_rule.action, CoreGroupAction):
167
+ if trigger_rule.action.add_members:
168
+ flow = ACTION_ADD_NODE_TO_GROUP
169
+ else:
170
+ flow = REMOVE_ADD_NODE_FROM_GROUP
171
+
172
+ workflow = ExecuteWorkflow(
173
+ workflow=flow,
174
+ parameters={
175
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
176
+ "group_id": trigger_rule.action.group_id,
177
+ "node_id": "{{ event.resource['infrahub.node.id'] }}",
178
+ "context": {
179
+ "__prefect_kind": "json",
180
+ "value": {
181
+ "__prefect_kind": "jinja",
182
+ "template": "{{ event.payload['context'] | tojson }}",
183
+ },
184
+ },
185
+ },
186
+ )
187
+
188
+ return cls(
189
+ name=trigger_rule.name,
190
+ trigger=event_trigger,
191
+ actions=[workflow],
192
+ )
193
+
194
+ @classmethod
195
+ def _from_group_trigger(
196
+ cls,
197
+ trigger_rule: CoreGroupTriggerRule,
198
+ ) -> Self:
199
+ event_trigger = EventTrigger()
200
+
201
+ if trigger_rule.members_added:
202
+ event_trigger.events.add(GroupMemberAddedEvent.event_name)
203
+ else:
204
+ event_trigger.events.add(GroupMemberRemovedEvent.event_name)
205
+
206
+ event_trigger.match = {"infrahub.node.kind": trigger_rule.group_kind, "infrahub.node.id": trigger_rule.group_id}
207
+
208
+ match trigger_rule.branch_scope:
209
+ case BranchScope.DEFAULT_BRANCH:
210
+ event_trigger.match["infrahub.branch.name"] = registry.default_branch
211
+ case BranchScope.OTHER_BRANCHES:
212
+ event_trigger.match["infrahub.branch.name"] = f"!{registry.default_branch}"
213
+
214
+ if isinstance(trigger_rule.action, CoreGeneratorAction):
215
+ workflow = ExecuteWorkflow(
216
+ workflow=ACTION_RUN_GENERATOR_GROUP_EVENT,
217
+ parameters={
218
+ "branch_name": "{{ event.resource['infrahub.branch.name'] }}",
219
+ "generator_definition_id": trigger_rule.action.generator_id,
220
+ "members": {
221
+ "__prefect_kind": "json",
222
+ "value": {
223
+ "__prefect_kind": "jinja",
224
+ "template": "{{ event.payload['data']['members']| tojson }}",
225
+ },
226
+ },
227
+ "context": {
228
+ "__prefect_kind": "json",
229
+ "value": {
230
+ "__prefect_kind": "jinja",
231
+ "template": "{{ event.payload['context'] | tojson }}",
232
+ },
233
+ },
234
+ },
235
+ )
236
+
237
+ return cls(
238
+ name=trigger_rule.name,
239
+ trigger=event_trigger,
240
+ actions=[workflow],
241
+ )
@@ -0,0 +1,104 @@
1
+ from typing import Any
2
+
3
+ from infrahub.core.constants import InfrahubKind
4
+
5
+ from .constants import BranchScope, 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
+ members_added = data["members_added"]["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
+ members_added=members_added,
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
+ add_members = data["add_members"]["value"]
74
+ group_id = data["group"]["node"]["id"]
75
+ return CoreGroupAction(add_members=add_members, 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
+ added=node["added"]["value"],
100
+ peer=node["peer"]["value"],
101
+ )
102
+ )
103
+
104
+ return matches