From b00c4e5d6d65053520641e9da8c276af8575c2e1 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Tue, 7 Jul 2026 11:20:12 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(backend)=20refactor=20analyt?= =?UTF-8?q?ics=20backend=20from=20Protocol=20to=20abstract=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch the analytics backend interface from a typing Protocol to an abstract base class, so the contract that backends must implement is explicit and enforced at instantiation time. --- CHANGELOG.md | 1 + src/backend/core/analytics/base.py | 15 +++++++-------- src/backend/core/analytics/posthog.py | 3 ++- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bafcbd2f..515e0237 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to - ⬆️(dependencies) update python dependencies - 💥(summary) remove v1 related code #1362 - ✨(meet) use compatible with summary v2 #1362 +- ♻️(backend) refactor analytics backend from Protocol to abstract class ### Fixed diff --git a/src/backend/core/analytics/base.py b/src/backend/core/analytics/base.py index 3c2b6b69..0f6ab388 100644 --- a/src/backend/core/analytics/base.py +++ b/src/backend/core/analytics/base.py @@ -1,12 +1,13 @@ """Analytics backend protocol and default no-op implementation.""" -from typing import Any, Protocol +from abc import ABC, abstractmethod +from typing import Any from ..models import User from .events import AnalyticsEvent -class AnalyticsBackend(Protocol): +class AnalyticsBackend(ABC): """ Interface every analytics backend must implement. @@ -17,11 +18,11 @@ class AnalyticsBackend(Protocol): ANALYTICS_BACKEND_SETTINGS = {"api_key": "...", "host": "..."} """ - def __init__(self, **kwargs: Any) -> None: ... - + @abstractmethod def identify(self, user: User, properties: dict[str, Any] | None = None) -> None: """Associate traits (email, name, ...) with an identified user.""" + @abstractmethod def capture( self, user: User, @@ -30,16 +31,14 @@ class AnalyticsBackend(Protocol): ) -> None: """Record an event performed by an identified user.""" + @abstractmethod def shutdown(self) -> None: """Flush pending events. Called on process exit.""" -class NoOpAnalytics: +class NoOpAnalytics(AnalyticsBackend): """Default backend: silently discards everything.""" - def __init__(self, **kwargs: Any) -> None: - """No-op: accepts and ignores any backend settings kwargs.""" - def identify(self, user: User, properties=None) -> None: """No-op: discards identify calls.""" diff --git a/src/backend/core/analytics/posthog.py b/src/backend/core/analytics/posthog.py index 1fef96b9..20b44ee8 100644 --- a/src/backend/core/analytics/posthog.py +++ b/src/backend/core/analytics/posthog.py @@ -6,12 +6,13 @@ from typing import Any from posthog import Posthog from ..models import User +from . import AnalyticsBackend from .events import AnalyticsEvent logger = logging.getLogger(__name__) -class PostHogAnalytics: +class PostHogAnalytics(AnalyticsBackend): """Send events to PostHog, keyed on the user's primary key (UUID).""" def __init__(