packages feed

asana-1.0.1.1: library/Asana/Api/CustomField.hs

module Asana.Api.CustomField
  ( CustomField (..),
    CustomFields (..),
    customEnumId,
    EnumOption (..),
    putCustomField,
    putCustomFields,
  )
where

import Asana.Api.Gid (Gid, gidToText)
import Asana.Api.Prelude
import Asana.Api.Request
import Data.Aeson
import Data.Aeson.Casing (aesonPrefix, snakeCase)
import Data.List (find)
import Data.Scientific (Scientific)
import Data.String (fromString)
import qualified Data.Text as T

data CustomField
  = CustomNumber Gid Text (Maybe Scientific)
  | CustomEnum Gid Text [EnumOption] (Maybe Text)
  | CustomText Gid Text (Maybe Text)
  | -- | Unexpected types dumped here
    Other
  deriving stock (Eq, Generic, Show)

newtype CustomFields = CustomFields {getCustomFields :: [CustomField]}
  deriving stock (Show, Eq)
  deriving newtype (FromJSON)

instance ToJSON CustomFields where
  toJSON (CustomFields fields) = object $ concatMap toPair fields
    where
      toPair = \case
        CustomNumber gid _ n -> [gidToKey gid .= n]
        e@(CustomEnum gid _ _ _) -> [gidToKey gid .= customEnumId e]
        _ -> []

      -- fromString will give us Text for aeson-1.x and Key for aeson-2.x
      gidToKey = fromString . T.unpack . gidToText

data EnumOption = EnumOption
  { eoGid :: Gid,
    eoName :: Text
  }
  deriving stock (Eq, Generic, Show)

instance FromJSON EnumOption where
  parseJSON = genericParseJSON $ aesonPrefix snakeCase

-- | Return a @'CustomField'@s value's Enum id, if possible
--
-- - Must be a @'CustomEnum'@
-- - Must have a value
-- - Must have an option with the same name as that value
customEnumId :: CustomField -> Maybe Gid
customEnumId (CustomEnum _ _ opts mValue) = do
  value <- mValue
  option <- find ((== value) . eoName) opts
  pure $ eoGid option
customEnumId _ = Nothing

instance FromJSON CustomField where
  parseJSON = withObject "CustomField" $ \o -> do
    oType <- o .: "type"

    case (oType :: Text) of
      "text" -> CustomText <$> o .: "gid" <*> o .: "name" <*> o .: "text_value"
      "number" ->
        CustomNumber <$> o .: "gid" <*> o .: "name" <*> o .: "number_value"
      "enum" -> do
        value <- o .: "enum_value"
        CustomEnum
          <$> (o .: "gid")
          <*> (o .: "name")
          <*> (o .: "enum_options")
          <*> case value of
            Object vo -> vo .:? "name"
            _ -> pure Nothing
      _ -> pure Other

putCustomField ::
  (MonadUnliftIO m, MonadLogger m, MonadReader env m, HasAsanaAccessKey env) =>
  Gid ->
  CustomField ->
  m ()
putCustomField taskId = putCustomFields taskId . CustomFields . pure

putCustomFields ::
  (MonadUnliftIO m, MonadLogger m, MonadReader env m, HasAsanaAccessKey env) =>
  Gid ->
  CustomFields ->
  m ()
putCustomFields taskId fields =
  void $
    put ("/tasks/" <> T.unpack (gidToText taskId)) $
      ApiData
        (object ["custom_fields" .= fields])