Skip to content

Commit

Permalink
Add Snowplow tracking for behavior flags (#10721)
Browse files Browse the repository at this point in the history
* add behavior deprecation snowplow callback
* update tests for new callback
* update test input with the new required field
  • Loading branch information
mikealfare authored Sep 18, 2024
1 parent 054c6fd commit d182d06
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 2 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/Under the Hood-20240911-162730.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Under the Hood
body: Add Snowplow tracking for behavior flag deprecations
time: 2024-09-11T16:27:30.293832-04:00
custom:
Author: mikealfare
Issue: "10552"
3 changes: 3 additions & 0 deletions core/dbt/events/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
from functools import partial
from typing import Callable, List

from dbt.tracking import track_behavior_change_warn
from dbt_common.events.base_types import EventLevel, EventMsg
from dbt_common.events.event_manager_client import (
add_callback_to_manager,
add_logger_to_manager,
cleanup_event_logger,
get_event_manager,
Expand Down Expand Up @@ -68,6 +70,7 @@ def setup_event_logger(flags, callbacks: List[Callable[[EventMsg], None]] = [])
make_log_dir_if_missing(flags.LOG_PATH)
event_manager = get_event_manager()
event_manager.callbacks = callbacks.copy()
add_callback_to_manager(track_behavior_change_warn)

if flags.LOG_LEVEL != "none":
line_format = _line_format_from_str(flags.LOG_FORMAT, LineFormat.PlainText)
Expand Down
18 changes: 17 additions & 1 deletion core/dbt/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
SendingEvent,
TrackingInitializeFailure,
)
from dbt_common.events.functions import fire_event, get_invocation_id
from dbt_common.events.base_types import EventMsg
from dbt_common.events.functions import fire_event, get_invocation_id, msg_to_dict
from dbt_common.exceptions import NotImplementedError

sp_logger.setLevel(100)
Expand All @@ -36,6 +37,7 @@

ADAPTER_INFO_SPEC = "iglu:com.dbt/adapter_info/jsonschema/1-0-1"
DEPRECATION_WARN_SPEC = "iglu:com.dbt/deprecation_warn/jsonschema/1-0-0"
BEHAVIOR_CHANGE_WARN_SPEC = "iglu:com.dbt/behavior_change_warn/jsonschema/1-0-0"
EXPERIMENTAL_PARSER = "iglu:com.dbt/experimental_parser/jsonschema/1-0-0"
INVOCATION_ENV_SPEC = "iglu:com.dbt/invocation_env/jsonschema/1-0-0"
INVOCATION_SPEC = "iglu:com.dbt/invocation/jsonschema/1-0-2"
Expand Down Expand Up @@ -364,6 +366,20 @@ def track_deprecation_warn(options):
)


def track_behavior_change_warn(msg: EventMsg) -> None:
if msg.info.name != "BehaviorChangeEvent" or active_user is None:
return

context = [SelfDescribingJson(BEHAVIOR_CHANGE_WARN_SPEC, msg_to_dict(msg))]
track(
active_user,
category="dbt",
action=msg.info.name,
label=get_invocation_id(),
context=context,
)


def track_invocation_end(invocation_context, result_type=None):
data = {"progress": "end", "result_type": result_type, "result": None}
data.update(invocation_context)
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/events/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_clears_preexisting_event_manager_state(self) -> None:

setup_event_logger(get_flags())
assert len(manager.loggers) == 0
assert len(manager.callbacks) == 0
assert len(manager.callbacks) == 1 # snowplow tracker for behavior flags

def test_specify_max_bytes(
self,
Expand Down
65 changes: 65 additions & 0 deletions tests/unit/test_behavior_flags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import pytest

from dbt.tracking import (
disable_tracking,
initialize_from_flags,
track_behavior_change_warn,
)
from dbt_common.behavior_flags import Behavior
from dbt_common.events.event_manager_client import (
add_callback_to_manager,
cleanup_event_logger,
)


@pytest.fixture
def snowplow_tracker(mocker):
# initialize `active_user` without writing the cookie to disk
initialize_from_flags(True, "")
mocker.patch("dbt.tracking.User.set_cookie").return_value = {"id": 42}

# add the relevant callback to the event manager
add_callback_to_manager(track_behavior_change_warn)

# don't make a call, catch the request
snowplow_tracker = mocker.patch("dbt.tracking.tracker.track_struct_event")

yield snowplow_tracker

# teardown
cleanup_event_logger()
disable_tracking()


def test_false_evaluation_triggers_snowplow_tracking(snowplow_tracker):
behavior = Behavior(
[{"name": "my_flag", "default": False, "description": "This is a false flag."}], {}
)
if behavior.my_flag:
# trigger a False evaluation
assert False, "This flag should evaluate to false and skip this line"
assert snowplow_tracker.called


def test_true_evaluation_does_not_trigger_snowplow_tracking(snowplow_tracker):
behavior = Behavior(
[{"name": "my_flag", "default": True, "description": "This is a true flag."}], {}
)
if behavior.my_flag:
pass
else:
# trigger a True evaluation
assert False, "This flag should evaluate to false and skip this line"
assert not snowplow_tracker.called


def test_false_evaluation_does_not_trigger_snowplow_tracking_when_disabled(snowplow_tracker):
disable_tracking()

behavior = Behavior(
[{"name": "my_flag", "default": False, "description": "This is a false flag."}], {}
)
if behavior.my_flag:
# trigger a False evaluation
assert False, "This flag should evaluate to false and skip this line"
assert not snowplow_tracker.called

0 comments on commit d182d06

Please sign in to comment.