110 lines
3.1 KiB
Haskell
110 lines
3.1 KiB
Haskell
{- | Module : ScriptExport.API
|
|
Maintainer : emi@haskell.fyi
|
|
Description: API for script exporter.
|
|
|
|
API for script exporter.
|
|
-}
|
|
module ScriptExport.API (
|
|
API,
|
|
runServer,
|
|
) where
|
|
|
|
import Codec.Serialise.Orphans ()
|
|
import Data.Aeson qualified as Aeson
|
|
import Data.Cache.Cached (cachedForM)
|
|
import Data.Function ((&))
|
|
import Data.Proxy (Proxy (Proxy))
|
|
import Data.Text (Text)
|
|
import GHC.Generics qualified as GHC
|
|
import Network.HTTP.Types qualified as Http
|
|
import Network.Wai qualified as Wai
|
|
import Network.Wai.Handler.Warp qualified as Warp
|
|
import Network.Wai.Middleware.Cors (CorsResourcePolicy (corsRequestHeaders), cors, simpleCorsResourcePolicy)
|
|
import Prettyprinter (Pretty (pretty), defaultLayoutOptions, hsep, layoutPretty, viaShow)
|
|
import Prettyprinter.Render.String (renderString)
|
|
import ScriptExport.Options (Options (..))
|
|
import ScriptExport.Types (Builders, ScriptQuery (ScriptQuery), runQuery)
|
|
import ScriptExport.Types qualified as Builders
|
|
import Servant.API (Capture, Get, JSON, Post, ReqBody, (:<|>) (..), type (:>))
|
|
import Servant.Server qualified as Servant
|
|
import System.Clock (TimeSpec (TimeSpec))
|
|
import Text.Printf (printf)
|
|
|
|
{- | Servant API type for script generation.
|
|
|
|
@since 0.2.0
|
|
-}
|
|
type API =
|
|
-- /query-script/:name
|
|
"query-script"
|
|
:> Capture "name" Text
|
|
:> ReqBody '[JSON] Aeson.Value
|
|
:> Post '[JSON] Aeson.Value
|
|
-- /info
|
|
:<|> "info"
|
|
:> Get '[JSON] ServerInfo
|
|
|
|
{- | Information about the server.
|
|
|
|
@since 0.2.0
|
|
-}
|
|
data ServerInfo = ServerInfo
|
|
{ revision :: Text
|
|
, exposedBuilders :: [Text]
|
|
}
|
|
deriving anyclass
|
|
( -- | @since 0.2.0
|
|
Aeson.ToJSON
|
|
, -- | @since 0.2.0
|
|
Aeson.FromJSON
|
|
)
|
|
deriving stock
|
|
( -- | @since 0.2.0
|
|
Show
|
|
, -- | @since 0.2.0
|
|
Eq
|
|
, -- | @since 0.2.0
|
|
GHC.Generic
|
|
)
|
|
|
|
-- | Run a Warp server that exposes a script generation endpoint.
|
|
runServer :: Text -> Builders -> Options -> IO ()
|
|
runServer revision builders options = do
|
|
let logger req status _maybeFileSize =
|
|
putStrLn . renderString . layoutPretty defaultLayoutOptions $
|
|
hsep
|
|
[ "[info]"
|
|
, viaShow $ Wai.requestMethod req
|
|
, viaShow $ Wai.rawPathInfo req
|
|
, "(" <> pretty (Http.statusCode status) <> ")"
|
|
]
|
|
|
|
settings =
|
|
Warp.defaultSettings
|
|
& Warp.setPort options.port
|
|
& Warp.setLogger logger
|
|
|
|
corsPolicy =
|
|
simpleCorsResourcePolicy
|
|
{ -- NOTE: Webpack dev server requires this for CORS workaround.
|
|
corsRequestHeaders = "content-type" : corsRequestHeaders simpleCorsResourcePolicy
|
|
}
|
|
|
|
corsMiddleware = cors . const $ Just corsPolicy
|
|
|
|
serverInfo =
|
|
ServerInfo
|
|
{ revision = revision
|
|
, exposedBuilders = Builders.toList builders
|
|
}
|
|
|
|
-- Scripts stay cached for five minutes
|
|
query <- cachedForM (Just $ TimeSpec 300 0) (`runQuery` builders)
|
|
|
|
let handler = (\name -> query . ScriptQuery name) :<|> pure serverInfo
|
|
|
|
printf "[info] Running 'agora-scripts' on :%d\n" (Warp.getPort settings)
|
|
|
|
Servant.serve (Proxy @API) handler
|
|
& corsMiddleware
|
|
& Warp.runSettings settings
|