packages feed

webgear-swagger-1.3.0: src/WebGear/Swagger/Trait/Header.hs

{-# OPTIONS_GHC -Wno-orphans #-}

-- | Swagger implementation of 'Header' trait.
module WebGear.Swagger.Trait.Header () where

import Control.Lens ((%~), (&), (.~), (<>~), (?~))
import Control.Monad.State.Strict (MonadState)
import qualified Data.HashMap.Strict.InsOrd as Map
import Data.Proxy (Proxy (Proxy))
import Data.String (fromString)
import Data.Swagger (
  Header,
  HeaderName,
  Param,
  ParamAnySchema (..),
  ParamLocation (..),
  ParamOtherSchema (..),
  Referenced (..),
  Response,
  Swagger,
  ToParamSchema (..),
  allOperations,
  description,
  headers,
  name,
  parameters,
  required,
  responses,
  schema,
 )
import Data.Swagger.Internal.Utils (swaggerMappend)
import Data.Text (Text)
import GHC.TypeLits (KnownSymbol, symbolVal)
import WebGear.Core.Handler (Description (..))
import WebGear.Core.Modifiers (Existence (..))
import WebGear.Core.Trait (Get (..), Set (..))
import qualified WebGear.Core.Trait.Header as WG
import WebGear.Swagger.Handler (Documentation, SwaggerHandler (..), consumeDescription)

instance
  ( KnownSymbol name
  , ToParamSchema val
  ) =>
  Get (SwaggerHandler m) (WG.RequestHeader Required ps name val)
  where
  {-# INLINE getTrait #-}
  getTrait WG.RequestHeader = SwaggerHandler $ addRequestHeader (Proxy @name) (Proxy @val) True

instance
  ( KnownSymbol name
  , ToParamSchema val
  ) =>
  Get (SwaggerHandler m) (WG.RequestHeader Optional ps name val)
  where
  {-# INLINE getTrait #-}
  getTrait WG.RequestHeader = SwaggerHandler $ addRequestHeader (Proxy @name) (Proxy @val) False

instance (KnownSymbol name) => Set (SwaggerHandler m) (WG.ResponseHeader Required name val) where
  {-# INLINE setTrait #-}
  setTrait WG.ResponseHeader _ = SwaggerHandler $ addResponseHeader (Proxy @name) (Proxy @val)

instance (KnownSymbol name) => Set (SwaggerHandler m) (WG.ResponseHeader Optional name val) where
  {-# INLINE setTrait #-}
  setTrait WG.ResponseHeader _ = SwaggerHandler $ addResponseHeader (Proxy @name) (Proxy @val)

addRequestHeader ::
  forall name val m.
  (KnownSymbol name, ToParamSchema val, MonadState Documentation m) =>
  Proxy name ->
  Proxy val ->
  Bool ->
  Swagger ->
  m Swagger
addRequestHeader _ _ isRequired doc = do
  desc <- consumeDescription
  let param =
        (mempty :: Param)
          & name .~ fromString @Text (symbolVal $ Proxy @name)
          & required ?~ isRequired
          & schema
            .~ ParamOther
              ( ParamOtherSchema
                  { _paramOtherSchemaIn = ParamHeader
                  , _paramOtherSchemaAllowEmptyValue = Just (not isRequired)
                  , _paramOtherSchemaParamSchema = toParamSchema (Proxy @val)
                  }
              )
          & description .~ fmap getDescription desc
  pure $ doc & allOperations . parameters <>~ [Inline param]

addResponseHeader ::
  forall name val m.
  (KnownSymbol name, MonadState Documentation m) =>
  Proxy name ->
  Proxy val ->
  Swagger ->
  m Swagger
addResponseHeader _ _ doc = do
  desc <- consumeDescription
  let headerName = fromString @HeaderName $ symbolVal $ Proxy @name
      header = mempty @Header & description .~ fmap getDescription desc
      resp = mempty @Response & headers <>~ [(headerName, header)]
  pure $
    if headerName == "Content-Type"
      then doc
      else doc & allOperations . responses . responses %~ Map.map (`swaggerMappend` Inline resp)