mirror of
https://github.com/Airsequel/AirGQL.git
synced 2025-07-22 16:14:04 +03:00
I removed the `readonlyGqlPostHandler` thingy from airgql, as it seemed to be a slightly worse version of the existing more general gql post handler. The one difference is that it was trying to use the readonly db directory, although the token already protected the queries to reading only, so this didn't matter. Moreover, the writeonly endpoint was also using the readonly directory, which seemed like a mistake (I removed that handler as well in favour of passing the `writeonly` access mode to the existing handler). I also fixed a TODO, making it so the readonly airsequel-cli handler takes the edition into account.
195 lines
5.1 KiB
Haskell
195 lines
5.1 KiB
Haskell
-- Necessary for servant-docs instances
|
|
{-# OPTIONS_GHC -Wno-orphans #-}
|
|
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
|
|
|
|
module Server.Server (platformAPI, platformApp)
|
|
where
|
|
|
|
import Protolude (
|
|
Int,
|
|
Monoid (mempty),
|
|
Proxy (Proxy),
|
|
($),
|
|
)
|
|
import Protolude qualified as P
|
|
|
|
import Data.Aeson (Object, Value, object)
|
|
import Data.Aeson.KeyMap qualified as KeyMap
|
|
import Data.ByteString.Lazy qualified as BL
|
|
import Data.Text (Text)
|
|
import Data.Text qualified as T
|
|
import Network.Wai (Application)
|
|
import Network.Wai.Parse (
|
|
defaultParseRequestBodyOptions,
|
|
setMaxRequestFilesSize,
|
|
setMaxRequestNumFiles,
|
|
)
|
|
import Servant (Context (EmptyContext, (:.)), NoContent)
|
|
import Servant.API (
|
|
Capture,
|
|
Get,
|
|
JSON,
|
|
PlainText,
|
|
Post,
|
|
ReqBody,
|
|
(:<|>) ((:<|>)),
|
|
(:>),
|
|
)
|
|
import Servant.Docs (
|
|
DocCapture (DocCapture),
|
|
ToCapture (toCapture),
|
|
ToSample,
|
|
singleSample,
|
|
toSamples,
|
|
)
|
|
import Servant.HTML.Blaze (HTML)
|
|
import Servant.Multipart (
|
|
MultipartData (MultipartData),
|
|
MultipartOptions (generalOptions),
|
|
Tmp,
|
|
ToMultipartSample (toMultipartSamples),
|
|
defaultMultipartOptions,
|
|
)
|
|
import Servant.Server (Server)
|
|
import Servant.Server qualified as Servant
|
|
import Text.Blaze.Internal (MarkupM)
|
|
|
|
import AirGQL.Config (Config (maxDbSize), defaultConfig)
|
|
import AirGQL.ExternalAppContext (ExternalAppContext)
|
|
import AirGQL.Lib (SQLPost, insertOnly, readOnly, writeOnly)
|
|
import AirGQL.Servant.Database (
|
|
apiDatabaseSchemaGetHandler,
|
|
apiDatabaseVacuumPostHandler,
|
|
)
|
|
import AirGQL.Servant.GraphQL (
|
|
gqlQueryGetHandler,
|
|
gqlQueryPostHandler,
|
|
playgroundDefaultQueryHandler,
|
|
)
|
|
import AirGQL.Servant.SqlQuery (sqlQueryPostHandler)
|
|
import AirGQL.Types.SchemaConf (SchemaConf (accessMode, pragmaConf), defaultSchemaConf)
|
|
import AirGQL.Types.SqlQueryPostResult (SqlQueryPostResult)
|
|
import AirGQL.Types.Types (GQLPost)
|
|
|
|
|
|
{- FOURMOLU_DISABLE -}
|
|
-- ATTENTION: Order of handlers matters!
|
|
type PlatformAPI =
|
|
-- gqlQueryGetHandler
|
|
-- Redirect to GraphiQL playground
|
|
"graphql" :> Get '[HTML] NoContent
|
|
|
|
-- gqlQueryPostHandler
|
|
:<|> "graphql"
|
|
:> ReqBody '[JSON] GQLPost
|
|
:> Post '[JSON] Object
|
|
|
|
:<|> "readonly" :> "graphql"
|
|
:> ReqBody '[JSON] GQLPost
|
|
:> Post '[JSON] Object
|
|
|
|
:<|> "writeonly" :> "graphql"
|
|
:> ReqBody '[JSON] GQLPost
|
|
:> Post '[JSON] Object
|
|
|
|
:<|> "insertonly" :> "graphql"
|
|
:> ReqBody '[JSON] GQLPost
|
|
:> Post '[JSON] Object
|
|
|
|
-- playgroundDefaultQueryHandler
|
|
:<|> "playground" :> "default-query"
|
|
:> Get '[PlainText] Text
|
|
|
|
-- apiDatabaseSchemaGetHandler
|
|
:<|> "schema" :> Get '[PlainText] Text
|
|
|
|
-- apiDatabaseVacuumPostHandler
|
|
:<|> "vacuum" :> Post '[JSON] Object
|
|
|
|
-- sqlQueryPostHandler
|
|
:<|> "sql"
|
|
:> ReqBody '[JSON] SQLPost
|
|
:> Post '[JSON] SqlQueryPostResult
|
|
|
|
{- FOURMOLU_ENABLE -}
|
|
|
|
|
|
-- | Instances for automatic documentation generation via servant-docs
|
|
instance ToSample (MultipartData Tmp) where
|
|
toSamples _ = singleSample $ MultipartData mempty mempty
|
|
|
|
|
|
instance ToMultipartSample Tmp (MultipartData Tmp) where
|
|
toMultipartSamples _ = []
|
|
|
|
|
|
instance ToSample Value where
|
|
toSamples _ = singleSample $ object []
|
|
|
|
|
|
instance ToSample (KeyMap.KeyMap Value) where
|
|
toSamples _ = singleSample $ KeyMap.fromList []
|
|
|
|
|
|
instance ToSample (MarkupM ()) where
|
|
toSamples _ = singleSample mempty
|
|
|
|
|
|
instance ToSample BL.ByteString where
|
|
toSamples _ = singleSample mempty
|
|
|
|
|
|
instance ToSample Text where
|
|
toSamples _ = singleSample mempty
|
|
|
|
|
|
instance ToSample P.ByteString where
|
|
toSamples _ = singleSample mempty
|
|
|
|
|
|
instance ToCapture (Capture "readonlyId" Text) where
|
|
toCapture _ = DocCapture "readonlyId" "Read-only ID of the database"
|
|
|
|
|
|
instance ToCapture (Capture "dbId" Text) where
|
|
toCapture _ = DocCapture "dbId" "ID of the database to be served"
|
|
|
|
|
|
platformAPI :: Proxy PlatformAPI
|
|
platformAPI = Proxy
|
|
|
|
|
|
platformServer :: ExternalAppContext -> P.FilePath -> Server PlatformAPI
|
|
platformServer ctx filePath = do
|
|
let dbPath = T.pack filePath
|
|
gqlQueryGetHandler dbPath
|
|
:<|> gqlQueryPostHandler defaultSchemaConf dbPath
|
|
:<|> gqlQueryPostHandler defaultSchemaConf{accessMode = readOnly} dbPath
|
|
:<|> gqlQueryPostHandler defaultSchemaConf{accessMode = writeOnly} dbPath
|
|
:<|> gqlQueryPostHandler defaultSchemaConf{accessMode = insertOnly} dbPath
|
|
:<|> playgroundDefaultQueryHandler dbPath
|
|
:<|> apiDatabaseSchemaGetHandler ctx dbPath
|
|
:<|> apiDatabaseVacuumPostHandler dbPath
|
|
:<|> sqlQueryPostHandler defaultSchemaConf.pragmaConf dbPath
|
|
|
|
|
|
platformApp :: ExternalAppContext -> P.FilePath -> Application
|
|
platformApp ctx filePath = do
|
|
let
|
|
maxFileSizeInByte :: Int = defaultConfig.maxDbSize
|
|
|
|
multipartOpts :: MultipartOptions Tmp
|
|
multipartOpts =
|
|
(defaultMultipartOptions (Proxy :: Proxy Tmp))
|
|
{ generalOptions =
|
|
setMaxRequestNumFiles 1 $
|
|
setMaxRequestFilesSize
|
|
(P.fromIntegral maxFileSizeInByte)
|
|
defaultParseRequestBodyOptions
|
|
}
|
|
|
|
context :: Context '[MultipartOptions Tmp]
|
|
context =
|
|
multipartOpts :. EmptyContext
|
|
|
|
Servant.serveWithContext platformAPI context $ platformServer ctx filePath
|