{-# LANGUAGE DataKinds #-} {-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Jordan.Servant.Query ( -- * Servant Combinators JordanQuery', OptionalJordanQuery, RequiredJordanQuery, -- * Using Jordan with query strings -- $queries -- ** Parsing Queries parseQueryAtKey, parseQueryAtKeyWith, hasQueryAtKey, -- ** Rendering queries renderQueryAtKey, renderQueryAtKeyWith, ) where import Data.Proxy (Proxy (..)) import GHC.TypeLits (Symbol) import Jordan.Servant.Query.Parse ( hasQueryAtKey, parseQueryAtKey, parseQueryAtKeyWith, ) import Jordan.Servant.Query.Render ( renderQueryAtKey, renderQueryAtKeyWith, ) import Servant.API.Modifiers (Required) -- $setup -- >>> :set -XDeriveGeneric -- >>> :set -XOverloadedStrings -- >>> :set -XTypeApplications -- >>> import GHC.Generics -- >>> import Jordan -- >>> import Network.HTTP.Types.URI -- >>> data Person = Person { firstName :: String, lastName :: String } deriving (Show, Read, Eq, Ord, Generic) -- >>> instance FromJSON Person -- >>> instance ToJSON Person -- $queries -- -- This module provides a way to use Jordan to parse to or render from query strings. -- -- An example is helpful: -- -- >>> renderQueryAtKey "person" (Person { firstName = "Rich", lastName = "Evans" }) -- [("person[firstName]",Just "Rich"),("person[lastName]",Just "Evans")] -- -- >>> renderQuery True $ renderQueryAtKey "person" (Person { firstName = "Rich", lastName = "Evans" }) -- "?person%5BfirstName%5D=Rich&person%5BlastName%5D=Evans" -- -- >>> parseQueryAtKey @Person "person" $ renderQueryAtKey "person" (Person { firstName = "Mike", lastName = "Stoklassa" }) -- Right (Person {firstName = "Mike", lastName = "Stoklassa"}) -- -- The format of parsed and rendered queries is designed to be \"similar enough\" to how Rails does it, which is -- also used in several other libraries. -- | A query argument at some key, that will be parsed via Jordan. -- If the query needs to contain nested data, it will all be nested under the same key. -- -- We do not support lenient queries as figuring out what to return in the case where the Jordan parser -- would have parsed nested keys is too difficult. -- -- Note: this type *does not* have a 'HasLink' instance, because unfortunately Servant is way too restrictive of what it exports, -- making such an instance impossible to write. I will open up a PR against Servant to fix this soon. data JordanQuery' (baseStr :: Symbol) (options :: [*]) (a :: *) -- | A query argument that is required. -- -- Will render an error message, in JSON format, if the query was bad in some way. type RequiredJordanQuery (baseStr :: Symbol) (a :: *) = JordanQuery' baseStr '[Required] a -- | A query argument that is *optional*. -- -- Will render an error message, in JSON format, if the query was bad in some way. type OptionalJordanQuery (baseStr :: Symbol) (a :: *) = JordanQuery' (baseStr :: Symbol) '[] (a :: *)