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

import Asana.Api.Prelude

import Asana.Api.Gid (Gid, gidToText)
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)
  | Other -- ^ Unexpected types dumped here
  deriving stock (CustomField -> CustomField -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CustomField -> CustomField -> Bool
$c/= :: CustomField -> CustomField -> Bool
== :: CustomField -> CustomField -> Bool
$c== :: CustomField -> CustomField -> Bool
Eq, forall x. Rep CustomField x -> CustomField
forall x. CustomField -> Rep CustomField x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep CustomField x -> CustomField
$cfrom :: forall x. CustomField -> Rep CustomField x
Generic, Int -> CustomField -> ShowS
[CustomField] -> ShowS
CustomField -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CustomField] -> ShowS
$cshowList :: [CustomField] -> ShowS
show :: CustomField -> String
$cshow :: CustomField -> String
showsPrec :: Int -> CustomField -> ShowS
$cshowsPrec :: Int -> CustomField -> ShowS
Show)

newtype CustomFields = CustomFields { CustomFields -> [CustomField]
getCustomFields :: [CustomField] }
  deriving stock (Int -> CustomFields -> ShowS
[CustomFields] -> ShowS
CustomFields -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CustomFields] -> ShowS
$cshowList :: [CustomFields] -> ShowS
show :: CustomFields -> String
$cshow :: CustomFields -> String
showsPrec :: Int -> CustomFields -> ShowS
$cshowsPrec :: Int -> CustomFields -> ShowS
Show, CustomFields -> CustomFields -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CustomFields -> CustomFields -> Bool
$c/= :: CustomFields -> CustomFields -> Bool
== :: CustomFields -> CustomFields -> Bool
$c== :: CustomFields -> CustomFields -> Bool
Eq)
  deriving newtype (Value -> Parser [CustomFields]
Value -> Parser CustomFields
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [CustomFields]
$cparseJSONList :: Value -> Parser [CustomFields]
parseJSON :: Value -> Parser CustomFields
$cparseJSON :: Value -> Parser CustomFields
FromJSON)

instance ToJSON CustomFields where
  toJSON :: CustomFields -> Value
toJSON (CustomFields [CustomField]
fields) = [Pair] -> Value
object forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap forall {a}. KeyValue a => CustomField -> [a]
toPair [CustomField]
fields
   where
    toPair :: CustomField -> [a]
toPair = \case
      CustomNumber Gid
gid Text
_ Maybe Scientific
n -> [forall {c}. IsString c => Gid -> c
gidToKey Gid
gid forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= Maybe Scientific
n]
      e :: CustomField
e@(CustomEnum Gid
gid Text
_ [EnumOption]
_ Maybe Text
_) -> [forall {c}. IsString c => Gid -> c
gidToKey Gid
gid forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= CustomField -> Maybe Gid
customEnumId CustomField
e]
      CustomField
_ -> []

    -- fromString will give us Text for aeson-1.x and Key for aeson-2.x
    gidToKey :: Gid -> c
gidToKey = forall a. IsString a => String -> a
fromString forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack forall b c a. (b -> c) -> (a -> b) -> a -> c
. Gid -> Text
gidToText

data EnumOption = EnumOption
  { EnumOption -> Gid
eoGid :: Gid
  , EnumOption -> Text
eoName :: Text
  }
  deriving stock (EnumOption -> EnumOption -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: EnumOption -> EnumOption -> Bool
$c/= :: EnumOption -> EnumOption -> Bool
== :: EnumOption -> EnumOption -> Bool
$c== :: EnumOption -> EnumOption -> Bool
Eq, forall x. Rep EnumOption x -> EnumOption
forall x. EnumOption -> Rep EnumOption x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep EnumOption x -> EnumOption
$cfrom :: forall x. EnumOption -> Rep EnumOption x
Generic, Int -> EnumOption -> ShowS
[EnumOption] -> ShowS
EnumOption -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [EnumOption] -> ShowS
$cshowList :: [EnumOption] -> ShowS
show :: EnumOption -> String
$cshow :: EnumOption -> String
showsPrec :: Int -> EnumOption -> ShowS
$cshowsPrec :: Int -> EnumOption -> ShowS
Show)

instance FromJSON EnumOption where
  parseJSON :: Value -> Parser EnumOption
parseJSON = forall a.
(Generic a, GFromJSON Zero (Rep a)) =>
Options -> Value -> Parser a
genericParseJSON forall a b. (a -> b) -> a -> b
$ ShowS -> Options
aesonPrefix ShowS
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 :: CustomField -> Maybe Gid
customEnumId (CustomEnum Gid
_ Text
_ [EnumOption]
opts Maybe Text
mValue) = do
  Text
value <- Maybe Text
mValue
  EnumOption
option <- forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((forall a. Eq a => a -> a -> Bool
== Text
value) forall b c a. (b -> c) -> (a -> b) -> a -> c
. EnumOption -> Text
eoName) [EnumOption]
opts
  forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ EnumOption -> Gid
eoGid EnumOption
option
customEnumId CustomField
_ = forall a. Maybe a
Nothing

instance FromJSON CustomField where
  parseJSON :: Value -> Parser CustomField
parseJSON = forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"CustomField" forall a b. (a -> b) -> a -> b
$ \Object
o -> do
    Text
oType <- Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"type"

    case (Text
oType :: Text) of
      Text
"text" -> Gid -> Text -> Maybe Text -> CustomField
CustomText forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"gid" forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"name" forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"text_value"
      Text
"number" ->
        Gid -> Text -> Maybe Scientific -> CustomField
CustomNumber forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"gid" forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"name" forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"number_value"
      Text
"enum" -> do
        Value
value <- Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"enum_value"
        Gid -> Text -> [EnumOption] -> Maybe Text -> CustomField
CustomEnum
          forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"gid")
          forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"name")
          forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (Object
o forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"enum_options")
          forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> case Value
value of
                Object Object
vo -> Object
vo forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"name"
                Value
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
      Text
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure CustomField
Other

putCustomField
  :: (MonadUnliftIO m, MonadLogger m, MonadReader env m, HasAsanaAccessKey env)
  => Gid
  -> CustomField
  -> m ()
putCustomField :: forall (m :: * -> *) env.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env) =>
Gid -> CustomField -> m ()
putCustomField Gid
taskId = forall (m :: * -> *) env.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env) =>
Gid -> CustomFields -> m ()
putCustomFields Gid
taskId forall b c a. (b -> c) -> (a -> b) -> a -> c
. [CustomField] -> CustomFields
CustomFields forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Applicative f => a -> f a
pure

putCustomFields
  :: (MonadUnliftIO m, MonadLogger m, MonadReader env m, HasAsanaAccessKey env)
  => Gid
  -> CustomFields
  -> m ()
putCustomFields :: forall (m :: * -> *) env.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env) =>
Gid -> CustomFields -> m ()
putCustomFields Gid
taskId CustomFields
fields =
  forall (f :: * -> *) a. Functor f => f a -> f ()
void forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, ToJSON a) =>
String -> a -> m Value
put (String
"/tasks/" forall a. Semigroup a => a -> a -> a
<> Text -> String
T.unpack (Gid -> Text
gidToText Gid
taskId)) forall a b. (a -> b) -> a -> b
$ forall a. a -> ApiData a
ApiData
    ([Pair] -> Value
object [Key
"custom_fields" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= CustomFields
fields])