Skip to content

Commit 4f92feb

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

6 files changed

Lines changed: 305 additions & 0 deletions

File tree

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,33 @@
5252
"items": {
5353
"$ref": "filter.json"
5454
}
55+
},
56+
"executionInitiator": {
57+
"type": "object",
58+
"description": "Information about what triggered this execution.",
59+
"properties": {
60+
"type": {
61+
"type": "string",
62+
"enum": ["display", "adhocExport", "automation", "alert"],
63+
"description": "The type of execution initiator."
64+
},
65+
"dashboardId": {
66+
"type": "string"
67+
},
68+
"visualizationId": {
69+
"type": "string"
70+
},
71+
"widgetId": {
72+
"type": "string"
73+
},
74+
"exportType": {
75+
"type": "string"
76+
},
77+
"automationId": {
78+
"type": "string"
79+
}
80+
},
81+
"required": ["type"]
5582
}
5683
},
5784
"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: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,125 @@ 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+
@none_safe
530+
def _dict_to_execution_initiator(d: dict) -> ExecutionInitiator:
531+
initiator_type = d.get("type")
532+
if initiator_type == "display":
533+
return ExecutionInitiatorDisplay(
534+
dashboard_id=d.get("dashboardId"),
535+
visualization_id=d.get("visualizationId"),
536+
widget_id=d.get("widgetId"),
537+
)
538+
if initiator_type == "adhocExport":
539+
return ExecutionInitiatorAdHocExport(
540+
export_type=d.get("exportType"),
541+
dashboard_id=d.get("dashboardId"),
542+
visualization_id=d.get("visualizationId"),
543+
widget_id=d.get("widgetId"),
544+
)
545+
if initiator_type == "automation":
546+
return ExecutionInitiatorAutomation(
547+
automation_id=d.get("automationId"),
548+
)
549+
if initiator_type == "alert":
550+
return ExecutionInitiatorAlert(
551+
dashboard_id=d.get("dashboardId"),
552+
visualization_id=d.get("visualizationId"),
553+
widget_id=d.get("widgetId"),
554+
)
555+
raise ValueError(f"Unsupported execution initiator type: {initiator_type}")
556+
557+
439558
def _dict_to_filter(d: dict) -> ExecutionContextFilter:
440559
filter_type = d.get("filterType")
441560
if filter_type == "positiveAttributeFilter":
@@ -542,6 +661,11 @@ class ExecutionContext:
542661
Only present if the execution type is "LABEL_ELEMENTS".
543662
"""
544663

664+
execution_initiator: Optional[ExecutionInitiator]
665+
"""
666+
Information about what triggered this execution (e.g. display, export, automation, alert).
667+
"""
668+
545669
@staticmethod
546670
@none_safe
547671
def from_dict(d: dict) -> "ExecutionContext":
@@ -563,6 +687,7 @@ def from_dict(d: dict) -> "ExecutionContext":
563687
),
564688
attributes=_dict_to_attributes(d.get("attributes", [])),
565689
filters=_dict_to_filters(d.get("filters", [])),
690+
execution_initiator=_dict_to_execution_initiator(d.get("executionInitiator")),
566691
)
567692

568693
@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"})

packages/gooddata-flexconnect/tests/json_schemas/test_execution_context_schema.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,69 @@
1616
"filters": [],
1717
"reportExecutionRequest": {},
1818
},
19+
# REPORT with executionInitiator display
20+
{
21+
"executionType": "REPORT",
22+
"organizationId": "org1",
23+
"workspaceId": "ws1",
24+
"userId": "user1",
25+
"attributes": [],
26+
"filters": [],
27+
"reportExecutionRequest": {},
28+
"executionInitiator": {
29+
"type": "display",
30+
"dashboardId": "d1",
31+
"visualizationId": "v1",
32+
"widgetId": "w1",
33+
},
34+
},
35+
# REPORT with executionInitiator adhocExport
36+
{
37+
"executionType": "REPORT",
38+
"organizationId": "org1",
39+
"workspaceId": "ws1",
40+
"userId": "user1",
41+
"attributes": [],
42+
"filters": [],
43+
"reportExecutionRequest": {},
44+
"executionInitiator": {
45+
"type": "adhocExport",
46+
"dashboardId": "d1",
47+
"visualizationId": "v1",
48+
"widgetId": "w1",
49+
"exportType": "CSV",
50+
},
51+
},
52+
# REPORT with executionInitiator automation
53+
{
54+
"executionType": "REPORT",
55+
"organizationId": "org1",
56+
"workspaceId": "ws1",
57+
"userId": "user1",
58+
"attributes": [],
59+
"filters": [],
60+
"reportExecutionRequest": {},
61+
"executionInitiator": {
62+
"type": "automation",
63+
"automationId": "a1",
64+
},
65+
},
66+
# REPORT with executionInitiator alert
67+
{
68+
"executionType": "REPORT",
69+
"organizationId": "org1",
70+
"workspaceId": "ws1",
71+
"userId": "user1",
72+
"attributes": [],
73+
"filters": [],
74+
"reportExecutionRequest": {},
75+
"executionInitiator": {
76+
"type": "alert",
77+
"dashboardId": "d1",
78+
"visualizationId": "v1",
79+
"widgetId": "w1",
80+
},
81+
},
1982
# minimal valid schema for LABEL_ELEMENTS execution type
2083
{
2184
"executionType": "LABEL_ELEMENTS",
@@ -110,6 +173,19 @@ def test_valid_execution_context_schema(value, get_validator):
110173
"filters": [],
111174
"labelElementsExecutionRequest": {"label": "label2"},
112175
},
176+
# invalid executionInitiator type
177+
{
178+
"executionType": "REPORT",
179+
"organizationId": "org1",
180+
"workspaceId": "ws1",
181+
"userId": "user1",
182+
"attributes": [],
183+
"filters": [],
184+
"reportExecutionRequest": {},
185+
"executionInitiator": {
186+
"type": "INVALID",
187+
},
188+
},
113189
],
114190
)
115191
def test_invalid_execution_context_schema(value, get_validator):

0 commit comments

Comments
 (0)