From 61743aec74aa67f7376941dbdb6d814e23fe3d10 Mon Sep 17 00:00:00 2001 From: Lucas <12496191+lucashuy@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:57:39 -0700 Subject: [PATCH 1/2] Added event tracking for feature --- samcli/commands/build/build_context.py | 18 ++++++++--- samcli/lib/telemetry/event.py | 1 + .../commands/buildcmd/test_build_context.py | 30 +++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/samcli/commands/build/build_context.py b/samcli/commands/build/build_context.py index a6d6b69102..de1c6442db 100644 --- a/samcli/commands/build/build_context.py +++ b/samcli/commands/build/build_context.py @@ -33,12 +33,12 @@ ) from samcli.lib.build.workflow_config import UnsupportedRuntimeException from samcli.lib.intrinsic_resolver.intrinsics_symbol_table import IntrinsicsSymbolTable -from samcli.lib.providers.provider import ResourcesToBuildCollector, Stack, Function, LayerVersion +from samcli.lib.providers.provider import ResourcesToBuildCollector, Stack, LayerVersion from samcli.lib.providers.sam_api_provider import SamApiProvider from samcli.lib.providers.sam_function_provider import SamFunctionProvider from samcli.lib.providers.sam_layer_provider import SamLayerProvider from samcli.lib.providers.sam_stack_provider import SamLocalStackProvider -from samcli.lib.telemetry.event import EventTracker +from samcli.lib.telemetry.event import EventTracker, UsedFeature, EventName from samcli.lib.utils.osutils import BUILD_DIR_PERMISSIONS from samcli.local.docker.manager import ContainerManager from samcli.local.lambdafn.exceptions import ( @@ -232,13 +232,15 @@ def __exit__(self, *args): def get_resources_to_build(self): return self.resources_to_build - def run(self): + def run(self) -> None: """Runs the building process by creating an ApplicationBuilder.""" if self._is_sam_template(): SamApiProvider.check_implicit_api_resource_ids(self.stacks) self._stacks = self._handle_build_pre_processing() + caught_exception = None + try: # boolean value indicates if mount with write or not, defaults to READ ONLY mount_with_write = False @@ -276,7 +278,7 @@ def run(self): self._check_rust_cargo_experimental_flag() for f in self.get_resources_to_build().functions: - EventTracker.track_event("BuildFunctionRuntime", f.runtime) + EventTracker.track_event(EventName.BUILD_FUNCTION_RUNTIME, f.runtime) self._build_result = builder.build() @@ -306,6 +308,8 @@ def run(self): click.secho(msg, fg="yellow") except FunctionNotFound as function_not_found_ex: + caught_exception = function_not_found_ex + raise UserException( str(function_not_found_ex), wrapped_from=function_not_found_ex.__class__.__name__ ) from function_not_found_ex @@ -317,6 +321,8 @@ def run(self): InvalidBuildGraphException, ResourceNotFound, ) as ex: + caught_exception = ex + click.secho("\nBuild Failed", fg="red") # Some Exceptions have a deeper wrapped exception that needs to be surfaced @@ -324,6 +330,10 @@ def run(self): deep_wrap = getattr(ex, "wrapped_from", None) wrapped_from = deep_wrap if deep_wrap else ex.__class__.__name__ raise UserException(str(ex), wrapped_from=wrapped_from) from ex + finally: + if self.build_in_source: + exception_name = type(caught_exception).__name__ if caught_exception else None + EventTracker.track_event(EventName.USED_FEATURE, UsedFeature.BUILD_IN_SOURCE, exception_name) def _is_sam_template(self) -> bool: """Check if a given template is a SAM template""" diff --git a/samcli/lib/telemetry/event.py b/samcli/lib/telemetry/event.py index 96e5fe45cb..398e8eeb5c 100644 --- a/samcli/lib/telemetry/event.py +++ b/samcli/lib/telemetry/event.py @@ -39,6 +39,7 @@ class UsedFeature(Enum): INIT_WITH_APPLICATION_INSIGHTS = "InitWithApplicationInsights" CFNLint = "CFNLint" INVOKED_CUSTOM_LAMBDA_AUTHORIZERS = "InvokedLambdaAuthorizers" + BUILD_IN_SOURCE = "BuildInSource" class EventType: diff --git a/tests/unit/commands/buildcmd/test_build_context.py b/tests/unit/commands/buildcmd/test_build_context.py index 417ce473e3..f128035cae 100644 --- a/tests/unit/commands/buildcmd/test_build_context.py +++ b/tests/unit/commands/buildcmd/test_build_context.py @@ -18,6 +18,7 @@ from samcli.lib.build.bundler import EsbuildBundlerManager from samcli.lib.build.workflow_config import UnsupportedRuntimeException from samcli.lib.providers.provider import Function, get_function_build_info +from samcli.lib.telemetry.event import EventName, UsedFeature from samcli.lib.utils.osutils import BUILD_DIR_PERMISSIONS from samcli.lib.utils.packagetype import ZIP, IMAGE from samcli.local.lambdafn.exceptions import FunctionNotFound @@ -1245,6 +1246,35 @@ def test_must_catch_function_not_found_exception( self.assertEqual(str(ctx.exception), "Function Not Found") + @patch("samcli.commands.build.build_context.BuildContext._is_sam_template") + @patch("samcli.commands.build.build_context.BuildContext.get_resources_to_build") + @patch("samcli.commands.build.build_context.BuildContext._check_exclude_warning") + @patch("samcli.commands.build.build_context.BuildContext._check_rust_cargo_experimental_flag") + @patch("samcli.lib.build.app_builder.ApplicationBuilder.build") + @patch("samcli.lib.telemetry.event.EventTracker.track_event") + def test_build_in_source_event_sent( + self, mock_track_event, mock_builder, mock_rust, mock_warning, mock_get_resources, mock_is_sam_template + ): + mock_builder.side_effect = [FunctionNotFound()] + + context = BuildContext( + resource_identifier="", + template_file="template_file", + base_dir="base_dir", + build_dir="build_dir", + cache_dir="cache_dir", + cached=False, + clean=False, + parallel=False, + mode="mode", + build_in_source=True, + ) + + with self.assertRaises(UserException): + context.run() + + mock_track_event.assert_called_with(EventName.USED_FEATURE, UsedFeature.BUILD_IN_SOURCE, "FunctionNotFound") + class TestBuildContext_is_sam_template(TestCase): @parameterized.expand( From 3fe84604752c0fdfa417d42c705e1f9a9171402c Mon Sep 17 00:00:00 2001 From: Lucas <12496191+lucashuy@users.noreply.github.com> Date: Thu, 31 Aug 2023 13:30:13 -0700 Subject: [PATCH 2/2] Correctly pass event value and fix typing --- samcli/commands/build/build_context.py | 8 +++++--- tests/unit/commands/buildcmd/test_build_context.py | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/samcli/commands/build/build_context.py b/samcli/commands/build/build_context.py index de1c6442db..7e2450a155 100644 --- a/samcli/commands/build/build_context.py +++ b/samcli/commands/build/build_context.py @@ -239,7 +239,7 @@ def run(self) -> None: self._stacks = self._handle_build_pre_processing() - caught_exception = None + caught_exception: Optional[Exception] = None try: # boolean value indicates if mount with write or not, defaults to READ ONLY @@ -278,7 +278,7 @@ def run(self) -> None: self._check_rust_cargo_experimental_flag() for f in self.get_resources_to_build().functions: - EventTracker.track_event(EventName.BUILD_FUNCTION_RUNTIME, f.runtime) + EventTracker.track_event(EventName.BUILD_FUNCTION_RUNTIME.value, f.runtime) self._build_result = builder.build() @@ -333,7 +333,9 @@ def run(self) -> None: finally: if self.build_in_source: exception_name = type(caught_exception).__name__ if caught_exception else None - EventTracker.track_event(EventName.USED_FEATURE, UsedFeature.BUILD_IN_SOURCE, exception_name) + EventTracker.track_event( + EventName.USED_FEATURE.value, UsedFeature.BUILD_IN_SOURCE.value, exception_name + ) def _is_sam_template(self) -> bool: """Check if a given template is a SAM template""" diff --git a/tests/unit/commands/buildcmd/test_build_context.py b/tests/unit/commands/buildcmd/test_build_context.py index f128035cae..5d48139d4b 100644 --- a/tests/unit/commands/buildcmd/test_build_context.py +++ b/tests/unit/commands/buildcmd/test_build_context.py @@ -1273,7 +1273,9 @@ def test_build_in_source_event_sent( with self.assertRaises(UserException): context.run() - mock_track_event.assert_called_with(EventName.USED_FEATURE, UsedFeature.BUILD_IN_SOURCE, "FunctionNotFound") + mock_track_event.assert_called_with( + EventName.USED_FEATURE.value, UsedFeature.BUILD_IN_SOURCE.value, "FunctionNotFound" + ) class TestBuildContext_is_sam_template(TestCase):