packages feed

kitchen-sink-0.1.0.0: src/KitchenSink/Core/Section/Payloads.hs

{- | This module exposes the key payloads that allow to convey data to the Engine.

JSON serialization instances exist for metadata consumption on frontend/api clients.
-}
module KitchenSink.Core.Section.Payloads
where

import Data.Aeson (FromJSON, ToJSON, Value)
import Data.Aeson qualified as Aeson
import Data.Char (toLower)
import Data.List qualified as List
import Data.Time.Clock (UTCTime)
import GHC.Generics (Generic)

import KitchenSink.Prelude

data PublicationStatus
    = Public
    | Upcoming
    | Archived
    deriving (Show, Eq, Generic)
instance FromJSON PublicationStatus
instance ToJSON PublicationStatus

data BuildInfoData = BuildInfoData
    { layout :: Text
    , publicationStatus :: Maybe PublicationStatus
    , robots :: Maybe Text
    , order :: Maybe Int
    -- ^ position among the pages of a same layout (used by the documentation layout)
    , route :: Maybe Text
    -- ^ SQLPage-style request path pattern (e.g. @\"\/users\/:id\"@) that turns
    -- this article into a request-time dynamic page (@layout: \"dynamic\"@),
    -- served only when @kitchen-sink serve --dynamic@ is used
    , rowCap :: Maybe Int
    -- ^ per-page cap on the number of rows a @.sql@ dataset of this page may
    -- return (default 1000); a dynamic-page-only setting
    , blobs :: Maybe Text
    -- ^ how @BLOB@ columns of this page's @.sql@ datasets are rendered into
    -- JSON: @\"base64\"@ (the default) or @\"omit\"@; a dynamic-page-only setting
    , auth :: Maybe Text
    -- ^ access policy for a dynamic page: @\"public\"@ (the default, when
    -- absent) or @\"required\"@, enforced before any @.sql@ dataset of the
    -- page runs; a dynamic-page-only setting, see "KitchenSink.Engine.Auth"
    }
    deriving (Show, Eq, Generic)
instance FromJSON BuildInfoData
instance ToJSON BuildInfoData

type TopicName = Text

data TopicData = TopicData
    { topics :: [TopicName]
    , keywords :: [Text]
    , imageLink :: Maybe Text
    , imageAlt :: Maybe Text
    }
    deriving (Show, Eq, Generic)
instance FromJSON TopicData
instance ToJSON TopicData

topicKeywords :: TopicData -> [Text]
topicKeywords d = List.nub $ topics d <> keywords d

data PreambleData = PreambleData
    { author :: Text
    , datetxt :: Maybe Text
    , date :: Maybe UTCTime
    , updated :: Maybe UTCTime
    , title :: Text
    , faviconUrl :: Maybe Text
    , lang :: Maybe Text
    }
    deriving (Show, Eq, Generic)
instance FromJSON PreambleData
instance ToJSON PreambleData

data SocialData = SocialData
    { twitter :: Maybe Text
    , linkedin :: Maybe Text
    , github :: Maybe Text
    , mastodon :: Maybe Text
    }
    deriving (Show, Eq, Generic)
instance FromJSON SocialData
instance ToJSON SocialData

data GeneratorInstructionsData = GeneratorInstructionsData
    { cmd :: Text
    , args :: [Text]
    , stdin :: Maybe Text
    , stdin_json :: Maybe Value
    , target :: FilePath
    }
    deriving (Show, Eq, Generic)
instance FromJSON GeneratorInstructionsData
instance ToJSON GeneratorInstructionsData

data GlossaryTerm = GlossaryTerm
    { term :: Text
    , definition :: Text
    }
    deriving (Show, Eq, Generic)
instance FromJSON GlossaryTerm
instance ToJSON GlossaryTerm

data GlossaryData = GlossaryData
    { glossary :: [GlossaryTerm]
    }
    deriving (Show, Eq, Generic)
instance FromJSON GlossaryData
instance ToJSON GlossaryData

-- | An inline admonition/callout box (note, warning, tip, ...) interleaved
-- with main-content sections. `calloutKind` is a free-form string mapped to
-- a CSS class (`callout-<kind>`); unrecognized kinds fall back to a default
-- style. JSON keys drop the `callout` prefix (`kind`/`title`/`body`) since
-- the Haskell field names would otherwise clash with `PreambleData.title`.
data CalloutData = CalloutData
    { calloutKind :: Text
    , calloutTitle :: Maybe Text
    , calloutBody :: Text
    }
    deriving (Show, Eq, Generic)
instance FromJSON CalloutData where
    parseJSON = Aeson.genericParseJSON calloutJsonOptions
instance ToJSON CalloutData where
    toJSON = Aeson.genericToJSON calloutJsonOptions

calloutJsonOptions :: Aeson.Options
calloutJsonOptions = Aeson.defaultOptions{Aeson.fieldLabelModifier = calloutFieldName}

calloutFieldName :: String -> String
calloutFieldName s = case List.stripPrefix "callout" s of
    Just (c : cs) -> toLower c : cs
    _ -> s

data FaqItem = FaqItem
    { question :: Text
    , answer :: Text
    }
    deriving (Show, Eq, Generic)
instance FromJSON FaqItem
instance ToJSON FaqItem

newtype FaqData = FaqData
    { items :: [FaqItem]
    }
    deriving (Show, Eq, Generic)
instance FromJSON FaqData
instance ToJSON FaqData

-- | One column of a pricing/comparison table. `values` must line up
-- positionally with `pricingFeatures` in the enclosing 'PricingTableData'.
data PricingPlan = PricingPlan
    { name :: Text
    , price :: Text
    , values :: [Text]
    }
    deriving (Show, Eq, Generic)
instance FromJSON PricingPlan
instance ToJSON PricingPlan

data PricingTableData = PricingTableData
    { pricingFeatures :: [Text]
    , pricingPlans :: [PricingPlan]
    }
    deriving (Show, Eq, Generic)
instance FromJSON PricingTableData
instance ToJSON PricingTableData