{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Provides orphan instances for Jordan servant combinators that allow the generation of documentation.
module Jordan.Servant.OpenApi where
import Control.Lens
import Data.OpenApi.Declare
import Data.OpenApi.Internal
import Data.OpenApi.Lens
import Data.OpenApi.Operation
import Data.OpenApi.ParamSchema
import Data.OpenApi.Schema
import Data.Proxy (Proxy (..))
import qualified Data.Text as T
import Data.Typeable
import GHC.TypeLits
import Jordan
import Jordan.OpenAPI
import Jordan.Servant
import Jordan.Types.JSONError
import Network.HTTP.Media
import Servant.API
import Servant.API.ContentTypes
import Servant.API.Modifiers
import Servant.OpenApi.Internal
instance (ToJSON a, Typeable a) => ToSchema (ViaJordan a) where
declareNamedSchema (Proxy :: Proxy (ViaJordan a)) = getToNamed (Proxy :: Proxy a)
instance forall a sub baseKey mods. (HasOpenApi sub, FromJSON a, KnownSymbol baseKey, SBoolI (FoldRequired mods)) => HasOpenApi (JordanQuery' baseKey mods a :> sub) where
toOpenApi _ =
toOpenApi (Proxy :: Proxy sub)
& addParam parameter
& setResponseWith (<>) 400 queryErrorResponse
& components . schemas %~ (<> defs)
where
(defs, ref) = runDeclare (getFromRef (Proxy @a)) mempty
parameter :: Param
parameter =
mempty
& name .~ T.pack (symbolVal $ Proxy @baseKey)
& in_ .~ ParamQuery
& style ?~ StyleDeepObject
& explode ?~ True
& allowReserved ?~ False
& schema ?~ ref
& required ?~ isRequired
queryErrorResponse =
pure $
mempty & content . at ("application" // "json+haskell-jordan-query-error") . non mempty . schema ?~ Inline s
s :: Schema
s = toParamSchema (Proxy :: Proxy String)
isRequired = case sbool @(FoldRequired mods) of
STrue -> True
SFalse -> False
errorResponse :: Declare (Definitions Schema) Response
errorResponse = do
ref <- getToRef (Proxy :: Proxy JSONError)
pure $ mempty & content . at ("application" // "json+haskell-servant-body-error") . non mempty . schema ?~ ref
instance forall a sub. (HasOpenApi sub, FromJSON a) => HasOpenApi (ReportingRequestBody a :> sub) where
toOpenApi _ =
toOpenApi (Proxy :: Proxy sub)
& addRequestBody reqBody
& addErrorResponse
& components . schemas %~ (<> defs)
where
(defs, ref) = runDeclare (getFromRef (Proxy :: Proxy a)) mempty
reqBody :: RequestBody =
mempty
{ _requestBodyContent = [("application" // "json", mediaType)]
}
mediaType :: MediaTypeObject =
mempty
{ _mediaTypeObjectSchema = Just ref
}
addErrorResponse = setResponseWith (<>) 400 errorResponse