mirror of
https://github.com/Airsequel/AirGQL.git
synced 2025-07-26 09:58:43 +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.
117 lines
2.9 KiB
Haskell
117 lines
2.9 KiB
Haskell
module AirGQL.Servant.GraphQL (
|
|
gqlQueryGetHandler,
|
|
gqlQueryPostHandler,
|
|
playgroundDefaultQueryHandler,
|
|
) where
|
|
|
|
import Protolude (
|
|
Applicative (pure),
|
|
MonadIO (liftIO),
|
|
Monoid (mempty),
|
|
Semigroup ((<>)),
|
|
($),
|
|
(&),
|
|
)
|
|
import Protolude qualified as P
|
|
|
|
import Control.Monad.Catch (catchAll)
|
|
import Data.Aeson (Object)
|
|
import Data.Text (Text)
|
|
import Data.Text qualified as T
|
|
import DoubleXEncoding (doubleXEncodeGql)
|
|
import Servant (NoContent, err303, errHeaders)
|
|
import Servant.Server qualified as Servant
|
|
import System.Directory (makeAbsolute)
|
|
|
|
import AirGQL.Lib (
|
|
column_name,
|
|
getColumns,
|
|
getTableNames,
|
|
)
|
|
import AirGQL.ServerUtils (executeQuery)
|
|
import AirGQL.Types.SchemaConf (SchemaConf)
|
|
import AirGQL.Types.Types (GQLPost (operationName, query, variables))
|
|
import AirGQL.Utils (
|
|
getDbDir,
|
|
getMainDbPath,
|
|
throwErr400WithMsg,
|
|
throwErr404WithMsg,
|
|
withRetryConn,
|
|
)
|
|
import System.FilePath (pathSeparator, takeDirectory)
|
|
|
|
|
|
gqlQueryGetHandler :: Text -> Servant.Handler NoContent
|
|
gqlQueryGetHandler dbId =
|
|
P.throwError
|
|
err303
|
|
{ errHeaders =
|
|
[("Location", P.encodeUtf8 $ "/dbs/" <> dbId <> "/graphiql")]
|
|
}
|
|
|
|
|
|
gqlQueryPostHandler
|
|
:: SchemaConf
|
|
-> Text
|
|
-> GQLPost
|
|
-> Servant.Handler Object
|
|
gqlQueryPostHandler schemaConf dbIdOrPath gqlPost = do
|
|
let
|
|
handleNoDbError :: P.SomeException -> Servant.Handler a
|
|
handleNoDbError excpetion = do
|
|
let errMsg = P.show excpetion
|
|
|
|
if "unable to open database file" `T.isInfixOf` errMsg
|
|
then
|
|
throwErr404WithMsg $
|
|
"Database \"" <> dbIdOrPath <> "\" does not exist"
|
|
else do
|
|
P.putErrLn $
|
|
"Error during execution of GraphQL query: " <> errMsg
|
|
throwErr400WithMsg errMsg
|
|
|
|
catchAll
|
|
( liftIO $ do
|
|
reqDir <-
|
|
if pathSeparator `T.elem` dbIdOrPath
|
|
then pure $ takeDirectory $ T.unpack dbIdOrPath
|
|
else makeAbsolute $ getDbDir dbIdOrPath
|
|
|
|
executeQuery
|
|
schemaConf
|
|
dbIdOrPath
|
|
reqDir
|
|
gqlPost.query
|
|
(gqlPost.variables & P.fromMaybe mempty)
|
|
gqlPost.operationName
|
|
)
|
|
handleNoDbError
|
|
|
|
|
|
playgroundDefaultQueryHandler
|
|
:: Text
|
|
-> Servant.Handler Text
|
|
playgroundDefaultQueryHandler dbId = do
|
|
liftIO $ withRetryConn (getMainDbPath dbId) $ \mainConn -> do
|
|
tableEntries <- getTableNames mainConn
|
|
|
|
case tableEntries of
|
|
(headTable : _) -> do
|
|
cols <- getColumns dbId mainConn headTable
|
|
pure $
|
|
P.fold
|
|
[ "query "
|
|
, doubleXEncodeGql headTable
|
|
, "Query {\n"
|
|
, " "
|
|
, doubleXEncodeGql headTable
|
|
, "( limit: 100 ) {\n"
|
|
, cols
|
|
& P.foldMap
|
|
( \col ->
|
|
" " <> doubleXEncodeGql col.column_name <> "\n"
|
|
)
|
|
, " }\n"
|
|
, "}"
|
|
]
|
|
_ -> pure ""
|