Skip to content

Commit c283a6f

Browse files
committed
feat: exec initiator into FlexConnect
JIRA: CQ-1944 risk: low
1 parent 2bdd0fc commit c283a6f

File tree

6 files changed

+355
-0
lines changed

6 files changed

+355
-0
lines changed

packages/gooddata-flexconnect/json_schemas/execution-context/execution-context.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,52 @@
5252
"items": {
5353
"$ref": "filter.json"
5454
}
55+
},
56+
"executionInitiator": {
57+
"type": "object",
58+
"description": "Information about what triggered this execution.",
59+
"required": ["type"],
60+
"oneOf": [
61+
{
62+
"description": "Execution triggered by displaying data in the UI.",
63+
"properties": {
64+
"type": { "const": "display" },
65+
"dashboardId": { "type": "string" },
66+
"visualizationId": { "type": "string" },
67+
"widgetId": { "type": "string" }
68+
},
69+
"additionalProperties": false
70+
},
71+
{
72+
"description": "Execution triggered by an ad-hoc export from the UI.",
73+
"properties": {
74+
"type": { "const": "adhocExport" },
75+
"dashboardId": { "type": "string" },
76+
"visualizationId": { "type": "string" },
77+
"widgetId": { "type": "string" },
78+
"exportType": { "type": "string" }
79+
},
80+
"additionalProperties": false
81+
},
82+
{
83+
"description": "Execution triggered by an automation.",
84+
"properties": {
85+
"type": { "const": "automation" },
86+
"automationId": { "type": "string" }
87+
},
88+
"additionalProperties": false
89+
},
90+
{
91+
"description": "Execution triggered by an alert evaluation.",
92+
"properties": {
93+
"type": { "const": "alert" },
94+
"dashboardId": { "type": "string" },
95+
"visualizationId": { "type": "string" },
96+
"widgetId": { "type": "string" }
97+
},
98+
"additionalProperties": false
99+
}
100+
]
55101
}
56102
},
57103
"allOf": [

packages/gooddata-flexconnect/src/gooddata_flexconnect/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
ExecutionContextNegativeAttributeFilter,
1515
ExecutionContextPositiveAttributeFilter,
1616
ExecutionContextRelativeDateFilter,
17+
ExecutionInitiator,
18+
ExecutionInitiatorAdHocExport,
19+
ExecutionInitiatorAlert,
20+
ExecutionInitiatorAutomation,
21+
ExecutionInitiatorDisplay,
1722
ExecutionRequest,
1823
ExecutionType,
1924
LabelElementsExecutionRequest,

packages/gooddata-flexconnect/src/gooddata_flexconnect/function/execution_context.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,126 @@ def from_dict(d: dict) -> "LabelElementsExecutionRequest":
436436
)
437437

438438

439+
@dataclass
440+
class ExecutionInitiatorDisplay:
441+
"""
442+
Information about an execution being run in order to display the data in the UI.
443+
"""
444+
445+
dashboard_id: Optional[str]
446+
"""
447+
The id of the dashboard the execution was run as a part of.
448+
"""
449+
450+
visualization_id: Optional[str]
451+
"""
452+
The id of the visualization the execution was run as a part of.
453+
"""
454+
455+
widget_id: Optional[str]
456+
"""
457+
The id of the widget the execution was run as a part of.
458+
"""
459+
460+
461+
@dataclass
462+
class ExecutionInitiatorAdHocExport:
463+
"""
464+
Information about an execution being run in order to export the data by a user in the UI.
465+
"""
466+
467+
dashboard_id: Optional[str]
468+
"""
469+
The id of the dashboard the execution was run as a part of.
470+
"""
471+
472+
visualization_id: Optional[str]
473+
"""
474+
The id of the visualization the execution was run as a part of.
475+
"""
476+
477+
widget_id: Optional[str]
478+
"""
479+
The id of the widget the execution was run as a part of.
480+
"""
481+
482+
export_type: Optional[str]
483+
"""
484+
The type of the exported file (CSV, RAW_CSV, etc.).
485+
"""
486+
487+
488+
@dataclass
489+
class ExecutionInitiatorAutomation:
490+
"""
491+
Information about an execution being run because of an automation.
492+
"""
493+
494+
automation_id: Optional[str]
495+
"""
496+
The id of the automation initiating this execution.
497+
"""
498+
499+
500+
@dataclass
501+
class ExecutionInitiatorAlert:
502+
"""
503+
Information about an execution being run in order to evaluate an alert.
504+
"""
505+
506+
dashboard_id: Optional[str]
507+
"""
508+
The id of the dashboard the execution was run as a part of.
509+
"""
510+
511+
visualization_id: Optional[str]
512+
"""
513+
The id of the visualization the execution was run as a part of.
514+
"""
515+
516+
widget_id: Optional[str]
517+
"""
518+
The id of the widget the execution was run as a part of.
519+
"""
520+
521+
522+
ExecutionInitiator: TypeAlias = Union[
523+
ExecutionInitiatorDisplay,
524+
ExecutionInitiatorAdHocExport,
525+
ExecutionInitiatorAutomation,
526+
ExecutionInitiatorAlert,
527+
]
528+
529+
530+
@none_safe
531+
def _dict_to_execution_initiator(d: dict) -> ExecutionInitiator:
532+
initiator_type = d.get("type")
533+
if initiator_type == "display":
534+
return ExecutionInitiatorDisplay(
535+
dashboard_id=d.get("dashboardId"),
536+
visualization_id=d.get("visualizationId"),
537+
widget_id=d.get("widgetId"),
538+
)
539+
if initiator_type == "adhocExport":
540+
return ExecutionInitiatorAdHocExport(
541+
export_type=d.get("exportType"),
542+
dashboard_id=d.get("dashboardId"),
543+
visualization_id=d.get("visualizationId"),
544+
widget_id=d.get("widgetId"),
545+
)
546+
if initiator_type == "automation":
547+
return ExecutionInitiatorAutomation(
548+
automation_id=d.get("automationId"),
549+
)
550+
if initiator_type == "alert":
551+
return ExecutionInitiatorAlert(
552+
dashboard_id=d.get("dashboardId"),
553+
visualization_id=d.get("visualizationId"),
554+
widget_id=d.get("widgetId"),
555+
)
556+
raise ValueError(f"Unsupported execution initiator type: {initiator_type}")
557+
558+
439559
def _dict_to_filter(d: dict) -> ExecutionContextFilter:
440560
filter_type = d.get("filterType")
441561
if filter_type == "positiveAttributeFilter":
@@ -542,6 +662,11 @@ class ExecutionContext:
542662
Only present if the execution type is "LABEL_ELEMENTS".
543663
"""
544664

665+
execution_initiator: Optional[ExecutionInitiator]
666+
"""
667+
Information about what triggered this execution (e.g. display, export, automation, alert).
668+
"""
669+
545670
@staticmethod
546671
@none_safe
547672
def from_dict(d: dict) -> "ExecutionContext":
@@ -563,6 +688,7 @@ def from_dict(d: dict) -> "ExecutionContext":
563688
),
564689
attributes=_dict_to_attributes(d.get("attributes", [])),
565690
filters=_dict_to_filters(d.get("filters", [])),
691+
execution_initiator=_dict_to_execution_initiator(d.get("executionInitiator")),
566692
)
567693

568694
@staticmethod

packages/gooddata-flexconnect/tests/function/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ def sample_report_execution_context_dict():
8888
}
8989
],
9090
"filters": [{"filterType": "negativeAttributeFilter", "labelIdentifier": "attribute1", "values": ["id1"]}],
91+
"executionInitiator": {
92+
"type": "display",
93+
"dashboardId": "b2f2d436-9831-4fe0-81df-8c59fd33242b",
94+
"visualizationId": "bf21d8ec-742c-48d7-8100-80663b43622b",
95+
"widgetId": "453844a7-4aa8-4456-be23-ac62b9b3b98a",
96+
},
9197
}
9298

9399

packages/gooddata-flexconnect/tests/function/test_flex_fun_execution_context.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
# (C) 2024 GoodData Corporation
2+
import pytest
3+
24
from gooddata_flexconnect.function.execution_context import (
35
ExecutionContext,
46
ExecutionContextAttribute,
57
ExecutionContextNegativeAttributeFilter,
8+
ExecutionInitiatorAdHocExport,
9+
ExecutionInitiatorAlert,
10+
ExecutionInitiatorAutomation,
11+
ExecutionInitiatorDisplay,
612
ExecutionType,
13+
_dict_to_execution_initiator,
714
)
815
from gooddata_sdk import (
916
Attribute,
@@ -35,6 +42,12 @@ def test_report_execution_context_deser(sample_report_execution_context_dict):
3542
assert isinstance(deserialized.attributes[0], ExecutionContextAttribute)
3643
assert isinstance(deserialized.filters[0], ExecutionContextNegativeAttributeFilter)
3744

45+
assert deserialized.execution_initiator is not None
46+
assert isinstance(deserialized.execution_initiator, ExecutionInitiatorDisplay)
47+
assert deserialized.execution_initiator.dashboard_id == "b2f2d436-9831-4fe0-81df-8c59fd33242b"
48+
assert deserialized.execution_initiator.visualization_id == "bf21d8ec-742c-48d7-8100-80663b43622b"
49+
assert deserialized.execution_initiator.widget_id == "453844a7-4aa8-4456-be23-ac62b9b3b98a"
50+
3851

3952
def test_label_elements_execution_context_deser(sample_label_execution_context_dict):
4053
"""
@@ -58,3 +71,56 @@ def test_label_elements_execution_context_deser(sample_label_execution_context_d
5871

5972
assert isinstance(deserialized.attributes[0], ExecutionContextAttribute)
6073
assert isinstance(deserialized.filters[0], ExecutionContextNegativeAttributeFilter)
74+
75+
assert deserialized.execution_initiator is None
76+
77+
78+
@pytest.mark.parametrize(
79+
"initiator_dict, expected_type",
80+
[
81+
(
82+
{
83+
"type": "display",
84+
"dashboardId": "d1",
85+
"visualizationId": "v1",
86+
"widgetId": "w1",
87+
},
88+
ExecutionInitiatorDisplay,
89+
),
90+
(
91+
{
92+
"type": "adhocExport",
93+
"dashboardId": "d1",
94+
"visualizationId": "v1",
95+
"widgetId": "w1",
96+
"exportType": "CSV",
97+
},
98+
ExecutionInitiatorAdHocExport,
99+
),
100+
(
101+
{
102+
"type": "automation",
103+
"automationId": "a1",
104+
},
105+
ExecutionInitiatorAutomation,
106+
),
107+
(
108+
{
109+
"type": "alert",
110+
"dashboardId": "d1",
111+
"visualizationId": "v1",
112+
"widgetId": "w1",
113+
},
114+
ExecutionInitiatorAlert,
115+
),
116+
],
117+
ids=["display", "adhocExport", "automation", "alert"],
118+
)
119+
def test_execution_initiator_deser(initiator_dict, expected_type):
120+
result = _dict_to_execution_initiator(initiator_dict)
121+
assert isinstance(result, expected_type)
122+
123+
124+
def test_execution_initiator_unknown_type():
125+
with pytest.raises(ValueError, match="Unsupported execution initiator type"):
126+
_dict_to_execution_initiator({"type": "unknown"})

0 commit comments

Comments
 (0)