packages feed

kansoku-api-0.3.0.0: src/Kansoku/Analysis/Types.hs

-- | Opaque, account-owned client documents. Kansoku never executes these values.
module Kansoku.Analysis.Types where

import Data.Aeson qualified as A
import Data.Int (Int64)
import Data.Text qualified as Text
import Data.UUID (UUID)
import Kansoku.Prelude
import Servant.API (FromHttpApiData (..), ToHttpApiData (..))
import Text.Read (readMaybe)

data AnalysisKind = Trends | Funnel | Retention
  deriving stock (Generic, Eq, Show)

kindText :: AnalysisKind -> Text
kindText = \case Trends -> "trends"; Funnel -> "funnel"; Retention -> "retention"

instance FromHttpApiData AnalysisKind where
  parseUrlPiece = \case
    "trends" -> Right Trends
    "funnel" -> Right Funnel
    "retention" -> Right Retention
    _ -> Left "kind: expected trends, funnel, or retention"

instance ToHttpApiData AnalysisKind where toUrlPiece = kindText

instance ToJSON AnalysisKind where toJSON = A.String . kindText

instance FromJSON AnalysisKind where
  parseJSON = A.withText "kind" (either (fail . Text.unpack) pure . parseUrlPiece)

-- | PostgreSQL bigint, always positive. Increment is checked by the store.
newtype Revision = Revision {unRevision :: Int64}
  deriving stock (Generic, Eq, Show)
  deriving newtype (ToJSON)

instance FromJSON Revision where
  parseJSON value = do
    n <- parseJSON value
    if n > 0 then pure (Revision n) else fail "revision: expected a positive bigint"

instance FromHttpApiData Revision where
  parseUrlPiece raw = case readMaybe (Text.unpack raw) of
    Just n | n > 0 -> Right (Revision n)
    _ -> Left "revision: expected a positive bigint"

instance ToHttpApiData Revision where toUrlPiece = Text.pack . show . unRevision

data AnalysisInput = AnalysisInput
  { name :: !Text,
    description :: !(Maybe Text),
    kind :: !AnalysisKind,
    documentVersion :: !Integer,
    document :: !A.Value
  }
  deriving stock (Generic, Eq, Show)
  deriving anyclass (FromJSON, ToJSON)

data AnalysisReplace = AnalysisReplace
  { name :: !Text,
    description :: !(Maybe Text),
    kind :: !AnalysisKind,
    documentVersion :: !Integer,
    document :: !A.Value,
    revision :: !Revision
  }
  deriving stock (Generic, Eq, Show)
  deriving anyclass (FromJSON, ToJSON)

replacementInput :: AnalysisReplace -> AnalysisInput
replacementInput AnalysisReplace {name, description, kind, documentVersion, document} = AnalysisInput {name, description, kind, documentVersion, document}

data SavedAnalysis = SavedAnalysis
  { id :: !UUID,
    name :: !Text,
    description :: !(Maybe Text),
    kind :: !AnalysisKind,
    documentVersion :: !Integer,
    document :: !A.Value,
    revision :: !Revision,
    createdAt :: !UTCTime,
    updatedAt :: !UTCTime
  }
  deriving stock (Generic, Eq, Show)
  deriving anyclass (FromJSON, ToJSON)