Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,33 @@
"items": {
"$ref": "filter.json"
}
},
"executionInitiator": {
"type": "object",
"description": "Information about what triggered this execution.",
"properties": {
"type": {
"type": "string",
"enum": ["display", "adhocExport", "automation", "alert"],
"description": "The type of execution initiator."
},
"dashboardId": {
"type": "string"
},
"visualizationId": {
"type": "string"
},
"widgetId": {
"type": "string"
},
"exportType": {
"type": "string"
},
"automationId": {
"type": "string"
}
},
"required": ["type"]
}
},
"allOf": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
ExecutionContextNegativeAttributeFilter,
ExecutionContextPositiveAttributeFilter,
ExecutionContextRelativeDateFilter,
ExecutionInitiator,
ExecutionInitiatorAdHocExport,
ExecutionInitiatorAlert,
ExecutionInitiatorAutomation,
ExecutionInitiatorDisplay,
ExecutionRequest,
ExecutionType,
LabelElementsExecutionRequest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,126 @@ def from_dict(d: dict) -> "LabelElementsExecutionRequest":
)


@dataclass
class ExecutionInitiatorDisplay:
"""
Information about an execution being run in order to display the data in the UI.
"""

dashboard_id: Optional[str]
"""
The id of the dashboard the execution was run as a part of.
"""

visualization_id: Optional[str]
"""
The id of the visualization the execution was run as a part of.
"""

widget_id: Optional[str]
"""
The id of the widget the execution was run as a part of.
"""


@dataclass
class ExecutionInitiatorAdHocExport:
"""
Information about an execution being run in order to export the data by a user in the UI.
"""

dashboard_id: Optional[str]
"""
The id of the dashboard the execution was run as a part of.
"""

visualization_id: Optional[str]
"""
The id of the visualization the execution was run as a part of.
"""

widget_id: Optional[str]
"""
The id of the widget the execution was run as a part of.
"""

export_type: Optional[str]
"""
The type of the exported file (CSV, RAW_CSV, etc.).
"""


@dataclass
class ExecutionInitiatorAutomation:
"""
Information about an execution being run because of an automation.
"""

automation_id: Optional[str]
"""
The id of the automation initiating this execution.
"""


@dataclass
class ExecutionInitiatorAlert:
"""
Information about an execution being run in order to evaluate an alert.
"""

dashboard_id: Optional[str]
"""
The id of the dashboard the execution was run as a part of.
"""

visualization_id: Optional[str]
"""
The id of the visualization the execution was run as a part of.
"""

widget_id: Optional[str]
"""
The id of the widget the execution was run as a part of.
"""


ExecutionInitiator: TypeAlias = Union[
ExecutionInitiatorDisplay,
ExecutionInitiatorAdHocExport,
ExecutionInitiatorAutomation,
ExecutionInitiatorAlert,
]


@none_safe
def _dict_to_execution_initiator(d: dict) -> ExecutionInitiator:
initiator_type = d.get("type")
if initiator_type == "display":
return ExecutionInitiatorDisplay(
dashboard_id=d.get("dashboardId"),
visualization_id=d.get("visualizationId"),
widget_id=d.get("widgetId"),
)
if initiator_type == "adhocExport":
return ExecutionInitiatorAdHocExport(
export_type=d.get("exportType"),
dashboard_id=d.get("dashboardId"),
visualization_id=d.get("visualizationId"),
widget_id=d.get("widgetId"),
)
if initiator_type == "automation":
return ExecutionInitiatorAutomation(
automation_id=d.get("automationId"),
)
if initiator_type == "alert":
return ExecutionInitiatorAlert(
dashboard_id=d.get("dashboardId"),
visualization_id=d.get("visualizationId"),
widget_id=d.get("widgetId"),
)
raise ValueError(f"Unsupported execution initiator type: {initiator_type}")


def _dict_to_filter(d: dict) -> ExecutionContextFilter:
filter_type = d.get("filterType")
if filter_type == "positiveAttributeFilter":
Expand Down Expand Up @@ -542,6 +662,11 @@ class ExecutionContext:
Only present if the execution type is "LABEL_ELEMENTS".
"""

execution_initiator: Optional[ExecutionInitiator]
"""
Information about what triggered this execution (e.g. display, export, automation, alert).
"""

@staticmethod
@none_safe
def from_dict(d: dict) -> "ExecutionContext":
Expand All @@ -563,6 +688,7 @@ def from_dict(d: dict) -> "ExecutionContext":
),
attributes=_dict_to_attributes(d.get("attributes", [])),
filters=_dict_to_filters(d.get("filters", [])),
execution_initiator=_dict_to_execution_initiator(d.get("executionInitiator")),
)

@staticmethod
Expand Down
6 changes: 6 additions & 0 deletions packages/gooddata-flexconnect/tests/function/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ def sample_report_execution_context_dict():
}
],
"filters": [{"filterType": "negativeAttributeFilter", "labelIdentifier": "attribute1", "values": ["id1"]}],
"executionInitiator": {
"type": "display",
"dashboardId": "b2f2d436-9831-4fe0-81df-8c59fd33242b",
"visualizationId": "bf21d8ec-742c-48d7-8100-80663b43622b",
"widgetId": "453844a7-4aa8-4456-be23-ac62b9b3b98a",
},
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
# (C) 2024 GoodData Corporation
import pytest

from gooddata_flexconnect.function.execution_context import (
ExecutionContext,
ExecutionContextAttribute,
ExecutionContextNegativeAttributeFilter,
ExecutionInitiatorAdHocExport,
ExecutionInitiatorAlert,
ExecutionInitiatorAutomation,
ExecutionInitiatorDisplay,
ExecutionType,
_dict_to_execution_initiator,
)
from gooddata_sdk import (
Attribute,
Expand Down Expand Up @@ -35,6 +42,12 @@ def test_report_execution_context_deser(sample_report_execution_context_dict):
assert isinstance(deserialized.attributes[0], ExecutionContextAttribute)
assert isinstance(deserialized.filters[0], ExecutionContextNegativeAttributeFilter)

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


def test_label_elements_execution_context_deser(sample_label_execution_context_dict):
"""
Expand All @@ -58,3 +71,56 @@ def test_label_elements_execution_context_deser(sample_label_execution_context_d

assert isinstance(deserialized.attributes[0], ExecutionContextAttribute)
assert isinstance(deserialized.filters[0], ExecutionContextNegativeAttributeFilter)

assert deserialized.execution_initiator is None


@pytest.mark.parametrize(
"initiator_dict, expected_type",
[
(
{
"type": "display",
"dashboardId": "d1",
"visualizationId": "v1",
"widgetId": "w1",
},
ExecutionInitiatorDisplay,
),
(
{
"type": "adhocExport",
"dashboardId": "d1",
"visualizationId": "v1",
"widgetId": "w1",
"exportType": "CSV",
},
ExecutionInitiatorAdHocExport,
),
(
{
"type": "automation",
"automationId": "a1",
},
ExecutionInitiatorAutomation,
),
(
{
"type": "alert",
"dashboardId": "d1",
"visualizationId": "v1",
"widgetId": "w1",
},
ExecutionInitiatorAlert,
),
],
ids=["display", "adhocExport", "automation", "alert"],
)
def test_execution_initiator_deser(initiator_dict, expected_type):
result = _dict_to_execution_initiator(initiator_dict)
assert isinstance(result, expected_type)


def test_execution_initiator_unknown_type():
with pytest.raises(ValueError, match="Unsupported execution initiator type"):
_dict_to_execution_initiator({"type": "unknown"})
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,69 @@
"filters": [],
"reportExecutionRequest": {},
},
# REPORT with executionInitiator display
{
"executionType": "REPORT",
"organizationId": "org1",
"workspaceId": "ws1",
"userId": "user1",
"attributes": [],
"filters": [],
"reportExecutionRequest": {},
"executionInitiator": {
"type": "display",
"dashboardId": "d1",
"visualizationId": "v1",
"widgetId": "w1",
},
},
# REPORT with executionInitiator adhocExport
{
"executionType": "REPORT",
"organizationId": "org1",
"workspaceId": "ws1",
"userId": "user1",
"attributes": [],
"filters": [],
"reportExecutionRequest": {},
"executionInitiator": {
"type": "adhocExport",
"dashboardId": "d1",
"visualizationId": "v1",
"widgetId": "w1",
"exportType": "CSV",
},
},
# REPORT with executionInitiator automation
{
"executionType": "REPORT",
"organizationId": "org1",
"workspaceId": "ws1",
"userId": "user1",
"attributes": [],
"filters": [],
"reportExecutionRequest": {},
"executionInitiator": {
"type": "automation",
"automationId": "a1",
},
},
# REPORT with executionInitiator alert
{
"executionType": "REPORT",
"organizationId": "org1",
"workspaceId": "ws1",
"userId": "user1",
"attributes": [],
"filters": [],
"reportExecutionRequest": {},
"executionInitiator": {
"type": "alert",
"dashboardId": "d1",
"visualizationId": "v1",
"widgetId": "w1",
},
},
# minimal valid schema for LABEL_ELEMENTS execution type
{
"executionType": "LABEL_ELEMENTS",
Expand Down Expand Up @@ -110,6 +173,19 @@ def test_valid_execution_context_schema(value, get_validator):
"filters": [],
"labelElementsExecutionRequest": {"label": "label2"},
},
# invalid executionInitiator type
{
"executionType": "REPORT",
"organizationId": "org1",
"workspaceId": "ws1",
"userId": "user1",
"attributes": [],
"filters": [],
"reportExecutionRequest": {},
"executionInitiator": {
"type": "INVALID",
},
},
],
)
def test_invalid_execution_context_schema(value, get_validator):
Expand Down
Loading