packages feed

instana-haskell-trace-sdk-0.10.0.0: test/shared/Instana/SDK/AgentStub/TraceRequest.hs

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE InstanceSigs      #-}
{-# LANGUAGE OverloadedStrings #-}
module Instana.SDK.AgentStub.TraceRequest where


import           Data.Aeson          (FromJSON, ToJSON, Value (Object), (.:),
                                      (.:?), (.=))
import qualified Data.Aeson          as Aeson
import qualified Data.HashMap.Strict as HM
import           Data.Map            (Map)
import           Data.Text           (Text)
import           GHC.Generics


type TraceRequest = [Span]


data From = From
  { entityId  :: String
  , agentUuid :: String
  } deriving (Eq, Generic, Show)


instance FromJSON From where
  parseJSON = Aeson.withObject "from" $
    \fr ->
      From
        <$> fr .: "e" -- entityId
        <*> fr .: "h" -- agent UUID/host ID


instance ToJSON From where
  toJSON :: From -> Value
  toJSON fr = Aeson.object
    [ "e" .= entityId fr
    , "h" .= agentUuid fr
    ]


data InstanaAncestor = InstanaAncestor
  { traceId  :: Maybe String
  , parentId :: Maybe String
  } deriving (Eq, Generic, Show)


instance FromJSON InstanaAncestor where
  parseJSON = Aeson.withObject "InstanaAncestor" $
    \obj ->
      InstanaAncestor
        <$> obj .: "t"
        <*> obj .: "p"


instance ToJSON InstanaAncestor where
  toJSON :: InstanaAncestor -> Value
  toJSON instanaAncestor = Aeson.object
    [ "t" .= traceId instanaAncestor
    , "p" .= parentId instanaAncestor
    ]


data Span =
  Span
    { t        :: String       -- traceId
    , s        :: String       -- spanId
    , p        :: Maybe String -- parentId
    , n        :: Text         -- spanName
    , ts       :: Int          -- timestamp
    , d        :: Int          -- duration
    , k        :: Int          -- kind
    , ec       :: Int          -- errorCount
    , ia       :: Maybe InstanaAncestor -- instana ancestor
    , tp       :: Maybe Bool   -- traceparent has been used for trace continuity
    , lt       :: Maybe String -- long trace ID
    , crtp     :: Maybe String -- correlation type
    , crid     :: Maybe String -- correlation id
    , sy       :: Maybe Bool   -- synthetic
    , spanData :: Aeson.Value  -- spanData
    , f        :: Maybe From   -- from
    } deriving (Eq, Show, Generic)


instance FromJSON Span where
  parseJSON = Aeson.withObject "span" $
    \decodedObject ->
      Span
        <$> decodedObject .:  "t"
        <*> decodedObject .:  "s"
        <*> decodedObject .:  "p"
        <*> decodedObject .:  "n"
        <*> decodedObject .:  "ts"
        <*> decodedObject .:  "d"
        <*> decodedObject .:  "k"
        <*> decodedObject .:  "ec"
        <*> decodedObject .:? "ia"
        <*> decodedObject .:? "tp"
        <*> decodedObject .:? "lt"
        <*> decodedObject .:  "crtp"
        <*> decodedObject .:  "crid"
        <*> decodedObject .:? "sy"
        <*> decodedObject .:  "data"
        <*> decodedObject .:  "f"


instance ToJSON Span where
  toJSON sp = Aeson.object
    [ "t"    .= t sp
    , "s"    .= s sp
    , "p"    .= p sp
    , "n"    .= n sp
    , "ts"   .= ts sp
    , "d"    .= d sp
    , "k"    .= k sp
    , "ec"   .= ec sp
    , "ia"   .= ia sp
    , "tp"   .= tp sp
    , "lt"   .= lt sp
    , "crtp" .= crtp sp
    , "crid" .= crid sp
    , "sy"   .= sy sp
    , "data" .= spanData sp
    , "f"    .= f sp
    ]


data SpanData = SpanData
  { httpAnnotations :: HttpAnnotations
  }
  deriving (Show)


instance FromJSON SpanData where
  parseJSON = Aeson.withObject "Span Annotations" $
    \obj ->
      SpanData
        <$> obj .: "http"


data HttpAnnotations = HttpAnnotations
  { method :: Maybe String
  , host   :: Maybe String
  , url    :: Maybe String
  , params :: Maybe String
  -- ("header",Object (fromList [("X-Response-Header-Downstream-To-App",String "Value 3"),("X-Request-Header-App-To-Downstream",String "Value 2")]))]))]),
  , header :: Maybe (Map String String)
  , status :: Maybe Int
  }
  deriving (Show)


instance FromJSON HttpAnnotations where
  parseJSON = Aeson.withObject "HTTP Annotations" $
    \obj ->
      HttpAnnotations
        <$> obj .:? "method"
        <*> obj .:? "host"
        <*> obj .:? "url"
        <*> obj .:? "params"
        <*> obj .:? "header"
        <*> obj .:? "status"


readSdkName :: Span -> Maybe Text
readSdkName span_ =
  let
    value = extractProperty ["sdk", "name"] (spanData span_)
  in
    case value of
      Just (Aeson.String sdkName) -> Just sdkName
      _                           -> Nothing


readService :: Span -> Maybe Text
readService span_ =
  let
    value = extractProperty ["service"] (spanData span_)
  in
    case value of
      Just (Aeson.String service) -> Just service
      _                           -> Nothing


extractProperty :: [Text] -> Value -> Maybe Value
extractProperty [] value              = Just value
extractProperty (key:keys) (Object o) = HM.lookup key o >>= extractProperty keys
extractProperty _      _              = Nothing