atlassian-connect-core-0.2.0.0: src/Snap/AtlassianConnect/Routes.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Snap.AtlassianConnect.Routes
( connectRoutes
, homeHandler
) where
import Control.Applicative
import qualified Control.Arrow as ARO
import Control.Monad (mzero)
import Control.Monad.State.Class (get)
import qualified Data.Aeson as A
import qualified Data.ByteString.Char8 as BC
import qualified Data.CaseInsensitive as CI
import Data.Connect.Descriptor as D
import Data.List
import Data.List.Split (splitOn)
import qualified Data.Map.Lazy as ML
import Data.Maybe (catMaybes, fromMaybe)
import qualified Network.HTTP.Media as NM
import qualified Snap.AtlassianConnect.Data as CD
import qualified Snap.AtlassianConnect.LifecycleResponse as CL
import qualified Snap.Core as SC
import qualified Snap.Helpers as SH
import qualified Snap.Snaplet as SS
data MediaType = ApplicationJson | TextHtml deriving (Eq)
newtype OrdMediaType = OMT NM.MediaType deriving (Eq, Show)
instance Ord OrdMediaType where
compare a b = compare (show a) (show b)
instance Show (MediaType) where
show ApplicationJson = "application/json"
show TextHtml = "text/html"
-- | In old versions of Atlassian Connect, if you queried the application root and your Accept headers wanted an
-- "application/json" response then they would return your Atlassian Connect descriptor and if the Accept headers
-- requested "text/html" then they returned your applications home page. This is a convenience method to enable that
-- behaviour to continue. If you use this method in your routes then you can make your home page behave in the same way.
-- This is not required for an Atlassian Connect application so feel free to just return home page content on your home
-- page and ignore this method.
homeHandler :: SS.Handler b CD.Connect () -> SS.Handler b CD.Connect ()
homeHandler sendHomePage = SC.method SC.GET handleGet <|> SH.respondWithError SH.badRequest "You can only GET the homepage."
where
handleGet = do
potentialAcceptHeader <- SC.getHeader bsAccept <$> SC.getRequest
case potentialAcceptHeader of
Just acceptHeader -> fromMaybe unknownHeader (NM.mapAcceptMedia mediaTypeMap acceptHeader)
Nothing -> mzero
mediaTypeMap =
[ (jsonMT, atlassianConnectHandler)
, (textHtmlMT, sendHomePage)
]
unknownHeader = SH.respondWithError SH.notFound "No response to a request with the provided Accept header."
jsonMT = "application" NM.// "json"
textHtmlMT = "text" NM.// "html"
bsAccept :: CI.CI BC.ByteString
bsAccept = CI.mk "Accept"
connectRoutes :: [(BC.ByteString, SS.Handler b CD.Connect ())]
connectRoutes = fmap (ARO.first BC.pack) simpleConnectRoutes
-- Handler b v a
-- b: lens from the base state to the current snaplets state (is the base state)
-- v: is the state of the current "view" snaplet (or simply, current state)
-- a: Monad return type
-- The MonadSnaplet type class distills the essence of the operations used with this pattern.
-- Its functions define fundamental methods for navigating snaplet trees.
simpleConnectRoutes :: [(String, SS.Handler b CD.Connect ())]
simpleConnectRoutes =
[ ("/atlassian-connect.json" , atlassianConnectHandler)
]
atlassianConnectHandler :: SS.Handler b CD.Connect ()
atlassianConnectHandler = do
connectData <- get
let dc = genDescriptorExtrasFromConnect connectData
SH.writeJson $ convertPluginDescriptor dc (CD.connectPlugin connectData)
genDescriptorExtrasFromConnect :: CD.Connect -> CD.DynamicDescriptorConfig
genDescriptorExtrasFromConnect connectData = CD.DynamicDescriptorConfig
{ CD.dcBaseUrl = CD.connectBaseUrl connectData
}
convertPluginDescriptor :: CD.DynamicDescriptorConfig -> D.Plugin -> D.Plugin
convertPluginDescriptor dc plugin = plugin
{ D.pluginBaseUrl = CD.dcBaseUrl dc
}