packages feed

cicero-api-0.1.1.3: src/IOHK/Cicero/API/Fact.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module IOHK.Cicero.API.Fact where

import Data.Coerce
import Data.Text
import Data.Aeson
import Data.Time.LocalTime
import Data.UUID
import Data.ByteString.Lazy
import Data.Binary.Builder
import Servant.API
import Servant.API.Generic
import Servant.API.NamedRoutes

import {-# SOURCE #-} IOHK.Cicero.API.Run (RunID)

newtype FactID = FactID { uuid :: UUID } deriving newtype (ToJSON, FromJSON, ToHttpApiData, Eq, Ord)

factIdFromString :: String -> Maybe FactID
factIdFromString = coerce . fromString

type API = NamedRoutes FactRoutes

-- | Fact routes in the Cicero API
data FactRoutes mode = FactRoutes
  { create :: mode :- ReqBody '[OctetStream] CreateFactV1 :> Post '[JSON] FactV1
  , getAll :: mode :- QueryParam' '[Required, Strict] "run" RunID :> Get '[JSON] [FactV1]
  } deriving stock Generic

data CreateFactV1 = CreateFact
  { -- | The data of the fact
    fact :: !Value
  , -- | Binary blob attached to the fact
    artifact :: !(Maybe ByteString)
  }

-- | This instance assumes that @'toEncoding' \@'Value'@ has no trailing whitespace!
instance MimeRender OctetStream CreateFactV1 where
  mimeRender _ cf = toLazyByteString $ factBuilt <> artifactBuilt
    where
      factBuilt = fromEncoding $ toEncoding cf.fact
      artifactBuilt = case cf.artifact of
        Just a -> fromLazyByteString a
        Nothing -> mempty

-- | A Cicero fact
data FactV1 = Fact
  { id :: !FactID
  , runId :: !(Maybe RunID)
  , createdAt :: !ZonedTime
  , value :: !Value
  , -- | The hash of the artifact, if any
    --
    -- This should be a proper hash type
    binaryHash :: !(Maybe Text)
  }

instance FromJSON FactV1 where
  parseJSON = withObject "FactV1" \o -> Fact
    <$> o .: "id"
    <*> o .:? "run_id"
    <*> o .: "created_at"
    <*> o .: "value"
    <*> o .:? "binary_hash"

instance ToJSON FactV1 where
  toJSON f = object $
      [ "id" .= f.id
      , "created_at" .= f.createdAt
      , "value" .= f.value
      ] ++ runIdFields ++ binaryHashFields
    where
      runIdFields = case f.runId of
        Just rid -> [ "run_id" .= rid ]
        Nothing -> []
      binaryHashFields = case f.binaryHash of
        Just hash -> [ "binary_hash" .= hash ]
        Nothing -> []
  toEncoding f = pairs $
      ( "id" .= f.id
     <> "created_at" .= f.createdAt
     <> "value" .= f.value
      ) <> runIdFields <> binaryHashFields
    where
      runIdFields = case f.runId of
        Just rid -> "run_id" .= rid
        Nothing -> mempty
      binaryHashFields = case f.binaryHash of
        Just hash -> "binary_hash" .= hash
        Nothing -> mempty