1
Fork 0
mirror of https://github.com/Airsequel/AirGQL.git synced 2025-07-26 09:58:43 +03:00
airgql/source/AirGQL/Servant/GraphQL.hs
prescientmoon 58e3f0bb7b Add insertonly endpoint to airgql server
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.
2024-12-07 16:47:58 +01:00

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 ""