packages feed

webgear-swagger-1.5.1: src/WebGear/Swagger/Trait/Status.hs

{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- | Swagger implementation of 'Status' trait.
module WebGear.Swagger.Trait.Status where

import Control.Applicative ((<|>))
import Control.Lens (at, mapped, (%~), (&), (.~), (?~), (^.))
import Data.Maybe (fromMaybe)
import Data.Swagger (
  Operation,
  PathItem,
  Referenced (..),
  Response,
  delete,
  description,
  get,
  head_,
  options,
  patch,
  paths,
  post,
  put,
  responses,
 )
import qualified Network.HTTP.Types as HTTP
import WebGear.Core.Handler (Description (..))
import qualified WebGear.Core.Response as WG
import WebGear.Core.Trait (Set, With, setTrait)
import WebGear.Core.Trait.Status (Status (..))
import WebGear.Swagger.Handler (SwaggerHandler (..), addRootPath, consumeDescription)

#if MIN_VERSION_swagger2(2,9,0)
import qualified Data.HashMap.Strict.InsOrd.Compat as Map
#else
import qualified Data.HashMap.Strict.InsOrd as Map
#endif

instance Set (SwaggerHandler m) Status where
  {-# INLINE setTrait #-}
  setTrait ::
    Status ->
    (WG.Response `With` ts -> WG.Response -> HTTP.Status -> WG.Response `With` (Status : ts)) ->
    SwaggerHandler m (WG.Response `With` ts, HTTP.Status) (WG.Response `With` (Status : ts))
  setTrait status _ = SwaggerHandler $ \doc -> do
    desc <- consumeDescription
    let doc' = if Map.null (doc ^. paths) then addRootPath doc else doc
    pure $ doc' & paths . mapped %~ setOperation desc status

setOperation :: Maybe Description -> Status -> PathItem -> PathItem
setOperation desc (Status status) item =
  item
    & delete %~ updateOperation
    & get %~ updateOperation
    & head_ %~ updateOperation
    & options %~ updateOperation
    & patch %~ updateOperation
    & post %~ updateOperation
    & put %~ updateOperation
  where
    httpCode = HTTP.statusCode status

    updateOperation :: Maybe Operation -> Maybe Operation
    updateOperation Nothing = Just $ mempty @Operation & at httpCode ?~ addDescription emptyResp
    updateOperation (Just op) =
      let resp = addDescription $ fromMaybe emptyResp $ (op ^. at httpCode) <|> (op ^. at 0)
       in Just $ op & responses . responses %~ Map.insert httpCode resp . Map.delete 0

    emptyResp :: Referenced Response
    emptyResp = Inline mempty

    addDescription :: Referenced Response -> Referenced Response
    addDescription (Ref r) = Ref r
    addDescription (Inline r) =
      case desc of
        Nothing -> Inline r
        Just (Description d) -> Inline (r & description .~ d)