{-# LANGUAGE FlexibleInstances #-}

-- | Internal entry point for emitting analytics events from `Task` code.
--
-- This module is intentionally `.Internal`. It is NOT re-exported from
-- the prelude's public `Platform` module. Higher layers (NoRedInk's
-- `Analytics.track`) wrap this and own the user-facing API; downstream
-- code that imports `Platform.Analytics.Internal` directly is bypassing
-- the closed event dictionary and should be flagged in review.
module Platform.Analytics.Internal
  ( trackEvent,
    AnalyticsEventDetails,
  )
where

import qualified Data.Aeson as Aeson
import NriPrelude
import qualified Platform
import qualified Platform.Internal as Internal
import qualified Prelude

-- | Send an analytics event. Opens a child tracing span named
-- @analytics.track@, attaches the JSON payload as the span's details,
-- and invokes the `LogHandler`'s analytics callback. The callback runs
-- as a `Task` with the current `LogHandler`, so any errors it logs flow
-- through the normal observability pipeline.
trackEvent :: (Aeson.ToJSON e) => e -> Task err ()
trackEvent :: forall e err. ToJSON e => e -> Task err ()
trackEvent e
event =
  let value :: Value
value = e -> Value
forall a. ToJSON a => a -> Value
Aeson.toJSON e
event
   in Text -> Task err () -> Task err ()
forall e a. HasCallStack => Text -> Task e a -> Task e a
Platform.tracingSpan Text
"analytics.track" (Task err () -> Task err ()) -> Task err () -> Task err ()
forall a b. (a -> b) -> a -> b
<| do
        AnalyticsEventDetails -> Task err ()
forall d e. TracingSpanDetails d => d -> Task e ()
Platform.setTracingSpanDetails (Value -> AnalyticsEventDetails
AnalyticsEventDetails Value
value)
        (LogHandler -> IO (Result err ())) -> Task err ()
forall x a. (LogHandler -> IO (Result x a)) -> Task x a
Internal.Task
          ( \LogHandler
handler -> do
              let Internal.Task LogHandler -> IO (Result Never ())
runCallback = LogHandler -> Value -> Task Never ()
Internal.trackAnalyticsEvent LogHandler
handler Value
value
              _ <- LogHandler -> IO (Result Never ())
runCallback LogHandler
handler
              Prelude.pure (Ok ())
          )

-- | A `TracingSpanDetails` wrapper around the analytics event payload, so
-- that the JSON we send to the analytics backend is also attached to the
-- @analytics.track@ span and visible in the existing observability
-- reporters.
newtype AnalyticsEventDetails = AnalyticsEventDetails Aeson.Value
  deriving ([AnalyticsEventDetails] -> Value
[AnalyticsEventDetails] -> Encoding
AnalyticsEventDetails -> Bool
AnalyticsEventDetails -> Value
AnalyticsEventDetails -> Encoding
(AnalyticsEventDetails -> Value)
-> (AnalyticsEventDetails -> Encoding)
-> ([AnalyticsEventDetails] -> Value)
-> ([AnalyticsEventDetails] -> Encoding)
-> (AnalyticsEventDetails -> Bool)
-> ToJSON AnalyticsEventDetails
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> (a -> Bool)
-> ToJSON a
$ctoJSON :: AnalyticsEventDetails -> Value
toJSON :: AnalyticsEventDetails -> Value
$ctoEncoding :: AnalyticsEventDetails -> Encoding
toEncoding :: AnalyticsEventDetails -> Encoding
$ctoJSONList :: [AnalyticsEventDetails] -> Value
toJSONList :: [AnalyticsEventDetails] -> Value
$ctoEncodingList :: [AnalyticsEventDetails] -> Encoding
toEncodingList :: [AnalyticsEventDetails] -> Encoding
$comitField :: AnalyticsEventDetails -> Bool
omitField :: AnalyticsEventDetails -> Bool
Aeson.ToJSON)

instance Internal.TracingSpanDetails AnalyticsEventDetails