Skip to content

Commit 092b59a

Browse files
committed
Refactor of webbook to support event_type and branch_scope
1 parent 76d0edb commit 092b59a

File tree

20 files changed

+651
-262
lines changed

20 files changed

+651
-262
lines changed

backend/infrahub/computed_attribute/triggers.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
trigger=EventTrigger(events={"infrahub.branch.created"}),
1313
actions=[
1414
ExecuteWorkflow(
15-
name=COMPUTED_ATTRIBUTE_SETUP_PYTHON.name,
15+
workflow=COMPUTED_ATTRIBUTE_SETUP_PYTHON,
1616
parameters={
1717
"branch_name": "{{ event.resource['infrahub.branch.name'] }}",
1818
"trigger_updates": False,
@@ -31,7 +31,7 @@
3131
trigger=EventTrigger(events={"infrahub.repository.update_commit"}),
3232
actions=[
3333
ExecuteWorkflow(
34-
name=COMPUTED_ATTRIBUTE_SETUP_PYTHON.name,
34+
workflow=COMPUTED_ATTRIBUTE_SETUP_PYTHON,
3535
parameters={
3636
"branch_name": "{{ event.resource['infrahub.branch.name'] }}",
3737
"commit": "{{ event.payload['commit'] }}",
@@ -50,7 +50,7 @@
5050
trigger=EventTrigger(events={"infrahub.branch.deleted"}),
5151
actions=[
5252
ExecuteWorkflow(
53-
name=COMPUTED_ATTRIBUTE_REMOVE_PYTHON.name,
53+
workflow=COMPUTED_ATTRIBUTE_REMOVE_PYTHON,
5454
parameters={
5555
"branch_name": "{{ event.resource['infrahub.branch.name'] }}",
5656
"context": {
@@ -67,7 +67,7 @@
6767
trigger=EventTrigger(events={"infrahub.schema.update"}),
6868
actions=[
6969
ExecuteWorkflow(
70-
name=COMPUTED_ATTRIBUTE_SETUP.name,
70+
workflow=COMPUTED_ATTRIBUTE_SETUP,
7171
parameters={
7272
"branch_name": "{{ event.resource['infrahub.branch.name'] }}",
7373
"context": {
@@ -77,7 +77,7 @@
7777
},
7878
),
7979
ExecuteWorkflow(
80-
name=COMPUTED_ATTRIBUTE_SETUP_PYTHON.name,
80+
workflow=COMPUTED_ATTRIBUTE_SETUP_PYTHON,
8181
parameters={
8282
"branch_name": "{{ event.resource['infrahub.branch.name'] }}",
8383
"context": {

backend/infrahub/core/constants/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,26 @@
4343

4444
NULL_VALUE = "NULL"
4545

46+
EVENT_NAMESPACE = "infrahub"
47+
48+
49+
class EventType(InfrahubStringEnum):
50+
BRANCH_CREATED = f"{EVENT_NAMESPACE}.branch.created"
51+
BRANCH_DELETED = f"{EVENT_NAMESPACE}.branch.deleted"
52+
BRANCH_MERGED = f"{EVENT_NAMESPACE}.branch.merged"
53+
BRANCH_REBASED = f"{EVENT_NAMESPACE}.branch.rebased"
54+
55+
SCHEMA_UPDATED = f"{EVENT_NAMESPACE}.schema.update"
56+
57+
NODE_CREATED = f"{EVENT_NAMESPACE}.node.created"
58+
NODE_UPDATED = f"{EVENT_NAMESPACE}.node.updated"
59+
NODE_DELETED = f"{EVENT_NAMESPACE}.node.deleted"
60+
61+
GROUP_MEMBER_ADDED = f"{EVENT_NAMESPACE}.group.member_added"
62+
GROUP_MEMBER_REMOVED = f"{EVENT_NAMESPACE}.group.member_removed"
63+
64+
REPOSITORY_UPDATE_COMMIT = f"{EVENT_NAMESPACE}.repository.update_commit"
65+
4666

4767
class PermissionLevel(enum.Flag):
4868
READ = 1

backend/infrahub/core/protocols.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ class CoreValidator(CoreNode):
197197

198198
class CoreWebhook(CoreNode):
199199
name: String
200+
event_type: Enum
201+
branch_scope: Dropdown
200202
description: StringOptional
201203
url: URL
202204
validate_certificates: BooleanOptional

backend/infrahub/core/schema/definitions/core/webhook.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
from infrahub.core.constants import (
2-
BranchSupportType,
3-
InfrahubKind,
4-
)
1+
from infrahub.core.constants import AllowOverrideType, BranchSupportType, EventType, InfrahubKind
52

63
core_webhook = {
74
"name": "Webhook",
@@ -12,11 +9,48 @@
129
"order_by": ["name__value"],
1310
"display_labels": ["name__value"],
1411
"include_in_menu": False,
12+
"icon": "mdi:webhook",
1513
"branch": BranchSupportType.AGNOSTIC.value,
1614
"uniqueness_constraints": [["name__value"]],
1715
"attributes": [
1816
{"name": "name", "kind": "Text", "unique": True, "order_weight": 1000},
19-
{"name": "description", "kind": "Text", "optional": True, "order_weight": 2000},
17+
{
18+
"name": "event_type",
19+
"kind": "Text",
20+
"enum": ["all"] + EventType.available_types(),
21+
"default_value": "all",
22+
"order_weight": 1500,
23+
"description": "The event type that triggers the webhook",
24+
},
25+
{
26+
"name": "branch_scope",
27+
"kind": "Dropdown",
28+
"choices": [
29+
{
30+
"name": "all_branches",
31+
"label": "All Branches",
32+
"description": "All branches",
33+
"color": "#fef08a",
34+
},
35+
{
36+
"name": "default_branch",
37+
"label": "Default Branch",
38+
"description": "Only the default branch",
39+
"color": "#86efac",
40+
},
41+
{
42+
"name": "other_branches",
43+
"label": "Other Branches",
44+
"description": "All branches except the default branch",
45+
"color": "#e5e7eb",
46+
},
47+
],
48+
"default_value": "default_branch",
49+
"optional": False,
50+
"order_weight": 2000,
51+
"allow_override": AllowOverrideType.NONE,
52+
},
53+
{"name": "description", "kind": "Text", "optional": True, "order_weight": 2500},
2054
{"name": "url", "kind": "URL", "order_weight": 3000},
2155
{
2256
"name": "validate_certificates",

backend/infrahub/menu/menu.py

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -261,32 +261,11 @@ def _extract_node_icon(model: MainSchemaTypes) -> str:
261261
namespace="Builtin",
262262
name="Webhooks",
263263
label="Webhooks",
264-
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.CUSTOMWEBHOOK)),
264+
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.WEBHOOK)),
265+
kind=InfrahubKind.WEBHOOK,
265266
protected=True,
266267
section=MenuSection.INTERNAL,
267268
order_weight=3000,
268-
children=[
269-
MenuItemDefinition(
270-
namespace="Builtin",
271-
name="WebhookStandard",
272-
label="Webhooks",
273-
kind=InfrahubKind.STANDARDWEBHOOK,
274-
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.STANDARDWEBHOOK)),
275-
protected=True,
276-
section=MenuSection.INTERNAL,
277-
order_weight=1000,
278-
),
279-
MenuItemDefinition(
280-
namespace="Builtin",
281-
name="WebhookCustom",
282-
label="Custom Webhooks",
283-
kind=InfrahubKind.CUSTOMWEBHOOK,
284-
icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.CUSTOMWEBHOOK)),
285-
protected=True,
286-
section=MenuSection.INTERNAL,
287-
order_weight=2000,
288-
),
289-
],
290269
),
291270
MenuItemDefinition(
292271
namespace="Builtin",

backend/infrahub/message_bus/operations/event/worker.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22

33
from infrahub.message_bus import messages
44
from infrahub.services import InfrahubServices
5-
from infrahub.workflows.catalogue import WEBHOOK_CONFIGURE
65

76

87
@flow(name="event-worker-newprimary-api")
98
async def new_primary_api(message: messages.EventWorkerNewPrimaryAPI, service: InfrahubServices) -> None:
109
service.log.info("api_worker promoted to primary", worker_id=message.worker_id)
1110

12-
await service.workflow.submit_workflow(workflow=WEBHOOK_CONFIGURE)
11+
# NOTE Should we remove this message ?

backend/infrahub/trigger/catalogue.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_BRANCH,
55
TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_COMMIT,
66
)
7+
from infrahub.trigger.models import TriggerDefinition
78
from infrahub.webhook.triggers import TRIGGER_WEBHOOK_SETUP_UPDATE
89

9-
triggers = [
10+
builtin_triggers: list[TriggerDefinition] = [
1011
TRIGGER_COMPUTED_ATTRIBUTE_ALL_SCHEMA,
1112
TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_CLEAN_BRANCH,
1213
TRIGGER_COMPUTED_ATTRIBUTE_PYTHON_SETUP_BRANCH,

backend/infrahub/trigger/models.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1+
from __future__ import annotations
2+
13
from datetime import timedelta
24
from enum import Enum
3-
from typing import Any
4-
from uuid import UUID
5+
from typing import TYPE_CHECKING, Any
56

67
from prefect.events.actions import RunDeployment
78
from prefect.events.schemas.automations import EventTrigger as PrefectEventTrigger
89
from prefect.events.schemas.automations import Posture
910
from prefect.events.schemas.events import ResourceSpecification
1011
from pydantic import BaseModel, Field
1112

13+
from infrahub.workflows.models import WorkflowDefinition # noqa: TC001
14+
1215
from .constants import NAME_SEPARATOR
1316

17+
if TYPE_CHECKING:
18+
from uuid import UUID
19+
1420

1521
class TriggerType(str, Enum):
1622
BUILTIN = "builtin"
23+
WEBHOOK = "webhook"
1724
# OBJECT = "object"
1825
# COMPUTED_ATTR = "computed_attr"
1926

@@ -35,19 +42,44 @@ def get_prefect(self) -> PrefectEventTrigger:
3542

3643

3744
class ExecuteWorkflow(BaseModel):
38-
name: str
45+
workflow: WorkflowDefinition
3946
parameters: dict[str, Any] = Field(default_factory=dict)
4047

48+
@property
49+
def name(self) -> str:
50+
return self.workflow.name
51+
4152
def get_prefect(self, mapping: dict[str, UUID]) -> RunDeployment:
4253
deployment_id = mapping[self.name]
54+
return self.get(deployment_id)
4355

56+
def get(self, id: UUID) -> RunDeployment:
4457
return RunDeployment(
4558
source="selected",
46-
deployment_id=deployment_id,
59+
deployment_id=id,
4760
parameters=self.parameters,
4861
job_variables={},
4962
)
5063

64+
def validate_parameters(self) -> None:
65+
if not self.parameters:
66+
return
67+
68+
workflow_params = self.workflow.get_parameters()
69+
workflow_required_params = [p.name for p in workflow_params.values() if p.required]
70+
trigger_params = list(self.parameters.keys())
71+
72+
missing_required_params = set(workflow_required_params) - set(trigger_params)
73+
wrong_params = set(trigger_params) - set(workflow_params)
74+
75+
if missing_required_params:
76+
raise ValueError(
77+
f"Missing required parameters: {missing_required_params} for workflow {self.workflow.name}"
78+
)
79+
80+
if wrong_params:
81+
raise ValueError(f"Workflow {self.workflow.name} doesn't support parameters: {wrong_params}")
82+
5183

5284
class TriggerDefinition(BaseModel):
5385
name: str
@@ -64,6 +96,10 @@ def get_deployment_names(self) -> list[str]:
6496
def generate_name(self) -> str:
6597
return f"{self.type.value}{NAME_SEPARATOR}{self.name}"
6698

99+
def validate_actions(self) -> None:
100+
for action in self.actions:
101+
action.validate_parameters()
102+
67103

68104
class BuiltinTriggerDefinition(TriggerDefinition):
69105
type: TriggerType = TriggerType.BUILTIN

backend/infrahub/trigger/tasks.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@
77
from prefect.client.orchestration import PrefectClient
88
from prefect.client.schemas.filters import DeploymentFilter, DeploymentFilterName
99

10-
from .catalogue import triggers
11-
from .constants import DEPRECATED_STATIC_TRIGGER_NAMES
10+
from infrahub.trigger.models import TriggerDefinition
11+
12+
# from .catalogue import triggers
1213
from .models import TriggerType
1314

1415
if TYPE_CHECKING:
1516
from uuid import UUID
1617

1718

18-
@task(name="trigger-setup", task_run_name="Setup triggers in task-manager", cache_policy=NONE) # type: ignore[arg-type]
19-
async def setup_triggers(client: PrefectClient) -> None:
19+
@task(name="trigger-setup", task_run_name="Setup triggers of type {trigger_type.value}", cache_policy=NONE) # type: ignore[arg-type]
20+
async def setup_triggers(
21+
client: PrefectClient,
22+
triggers: list[TriggerDefinition],
23+
trigger_type: TriggerType = TriggerType.BUILTIN,
24+
deprecated_triggers: list[str] | None = None,
25+
) -> None:
2026
log = get_run_logger()
2127

2228
# -------------------------------------------------------------
@@ -32,15 +38,15 @@ async def setup_triggers(client: PrefectClient) -> None:
3238
deployments_mapping: dict[str, UUID] = {name: item.id for name, item in deployments.items()}
3339
existing_automations = {item.name: item for item in await client.read_automations()}
3440

35-
builtin_automations = [
36-
item.name for item in await client.read_automations() if item.name.startswith(TriggerType.BUILTIN.value)
41+
trigger_automations = [
42+
item.name for item in await client.read_automations() if item.name.startswith(trigger_type.value)
3743
]
3844
trigger_names = [trigger.generate_name() for trigger in triggers]
3945

40-
_, to_delete, _ = compare_lists(list1=builtin_automations, list2=trigger_names)
46+
to_delete = set(trigger_automations) - set(trigger_names)
4147

4248
# -------------------------------------------------------------
43-
# Create or Update all builtin triggers
49+
# Create or Update all triggers
4450
# -------------------------------------------------------------
4551
for trigger in triggers:
4652
automation = AutomationCore(
@@ -61,7 +67,7 @@ async def setup_triggers(client: PrefectClient) -> None:
6167
log.info(f"{trigger.name} Created")
6268

6369
# -------------------------------------------------------------
64-
# Delete Builtin Triggers that shouldn't be there
70+
# Delete Triggers that shouldn't be there
6571
# -------------------------------------------------------------
6672
for item_to_delete in to_delete:
6773
existing_automation = existing_automations.get(item_to_delete)
@@ -75,11 +81,12 @@ async def setup_triggers(client: PrefectClient) -> None:
7581
# -------------------------------------------------------------
7682
# Delete Deprecated triggers
7783
# -------------------------------------------------------------
78-
for trigger_name in DEPRECATED_STATIC_TRIGGER_NAMES:
79-
existing_automation = existing_automations.get(trigger_name)
84+
if deprecated_triggers:
85+
for trigger_name in deprecated_triggers:
86+
existing_automation = existing_automations.get(trigger_name)
8087

81-
if not existing_automation:
82-
continue
88+
if not existing_automation:
89+
continue
8390

84-
await client.delete_automation(automation_id=existing_automation.id)
85-
log.info(f"{trigger_name} Deleted")
91+
await client.delete_automation(automation_id=existing_automation.id)
92+
log.info(f"{trigger_name} Deleted")

0 commit comments

Comments
 (0)