mirror of
https://github.com/Airsequel/AirGQL.git
synced 2025-09-18 19:34:32 +02:00
Rename a few fields to match the Hasura api
This commit is contained in:
parent
41f1752983
commit
d54896341a
4 changed files with 219 additions and 102 deletions
source/AirGQL
|
@ -40,6 +40,7 @@ import Protolude (
|
||||||
(<=),
|
(<=),
|
||||||
(>),
|
(>),
|
||||||
(>=),
|
(>=),
|
||||||
|
(||),
|
||||||
)
|
)
|
||||||
import Protolude qualified as P
|
import Protolude qualified as P
|
||||||
|
|
||||||
|
@ -213,33 +214,36 @@ getColNamesQuoted columnEntries =
|
||||||
opAndValToSql :: HashMap.HashMap Text Value -> [Text]
|
opAndValToSql :: HashMap.HashMap Text Value -> [Text]
|
||||||
opAndValToSql operatorAndValue =
|
opAndValToSql operatorAndValue =
|
||||||
case HashMap.toList operatorAndValue of
|
case HashMap.toList operatorAndValue of
|
||||||
[("eq", value)] ->
|
[(op, value)]
|
||||||
pure $
|
| op == "eq" || op == "_eq" ->
|
||||||
if value == Null
|
pure $
|
||||||
then " IS NULL"
|
if value == Null
|
||||||
else " == " <> gqlValueToSQLText value
|
then " IS NULL"
|
||||||
[("neq", value)] ->
|
else " == " <> gqlValueToSQLText value
|
||||||
if value == Null
|
| op == "neq" || op == "_neq" ->
|
||||||
then pure " IS NOT NULL"
|
if value == Null
|
||||||
else
|
then pure " IS NOT NULL"
|
||||||
[ " != " <> gqlValueToSQLText value
|
else
|
||||||
, " IS NULL"
|
[ " != " <> gqlValueToSQLText value
|
||||||
]
|
, " IS NULL"
|
||||||
[("in", List values)] ->
|
]
|
||||||
let listValues = values <&> gqlValueToSQLText & intercalate ","
|
| op == "in" || op == "_in"
|
||||||
in [" IN (" <> listValues <> ")"]
|
, List values <- value ->
|
||||||
[("nin", List values)] ->
|
let listValues = values <&> gqlValueToSQLText & intercalate ","
|
||||||
let listValues = values <&> gqlValueToSQLText & intercalate ","
|
in [" IN (" <> listValues <> ")"]
|
||||||
in [" NOT IN (" <> listValues <> ")"]
|
| op == "nin" || op == "_nin"
|
||||||
<> if P.elem Null values
|
, List values <- value ->
|
||||||
then []
|
let listValues = values <&> gqlValueToSQLText & intercalate ","
|
||||||
else [" IS NULL"]
|
in [" NOT IN (" <> listValues <> ")"]
|
||||||
[("gt", value)] -> [" > " <> gqlValueToSQLText value]
|
<> if P.elem Null values
|
||||||
[("gte", value)] -> [" >= " <> gqlValueToSQLText value]
|
then []
|
||||||
[("lt", value)] -> [" < " <> gqlValueToSQLText value]
|
else [" IS NULL"]
|
||||||
[("lte", value)] -> [" <= " <> gqlValueToSQLText value]
|
| op == "gt" || op == "_gt" -> [" > " <> gqlValueToSQLText value]
|
||||||
[("like", value)] -> [" like " <> gqlValueToSQLText value]
|
| op == "gte" || op == "_gte" -> [" >= " <> gqlValueToSQLText value]
|
||||||
[("ilike", value)] -> [" like " <> gqlValueToSQLText value]
|
| op == "lt" || op == "_lt" -> [" < " <> gqlValueToSQLText value]
|
||||||
|
| op == "lte" || op == "_lte" -> [" <= " <> gqlValueToSQLText value]
|
||||||
|
| op == "like" || op == "_like" -> [" like " <> gqlValueToSQLText value]
|
||||||
|
| op == "ilike" || op == "_ilike" -> [" like " <> gqlValueToSQLText value]
|
||||||
filter -> do
|
filter -> do
|
||||||
throw $
|
throw $
|
||||||
userError $
|
userError $
|
||||||
|
@ -451,16 +455,21 @@ rowsToGraphQL dbId table updatedRows =
|
||||||
& List
|
& List
|
||||||
|
|
||||||
|
|
||||||
|
-- Attempts to find and decode an argument, given its current name, and a list
|
||||||
|
-- of deprecated older names.
|
||||||
tryGetArg
|
tryGetArg
|
||||||
:: forall m a
|
:: forall m a
|
||||||
. (FromGraphQL a)
|
. (FromGraphQL a)
|
||||||
=> (MonadIO m)
|
=> (MonadIO m)
|
||||||
=> Text
|
=> Text
|
||||||
|
-> [Text]
|
||||||
-> HashMap Text Value
|
-> HashMap Text Value
|
||||||
-> m (Maybe a)
|
-> m (Maybe a)
|
||||||
tryGetArg name args = do
|
tryGetArg name alts args = do
|
||||||
case HashMap.lookup name args of
|
case HashMap.lookup name args of
|
||||||
Nothing -> pure Nothing
|
Nothing -> case alts of
|
||||||
|
alt : others -> tryGetArg alt others args
|
||||||
|
[] -> pure Nothing
|
||||||
Just value ->
|
Just value ->
|
||||||
case fromGraphQL value of
|
case fromGraphQL value of
|
||||||
Just decoded -> pure $ Just decoded
|
Just decoded -> pure $ Just decoded
|
||||||
|
@ -470,15 +479,17 @@ tryGetArg name args = do
|
||||||
"Argument " <> T.unpack name <> " has invalid format"
|
"Argument " <> T.unpack name <> " has invalid format"
|
||||||
|
|
||||||
|
|
||||||
|
-- Similar to `tryGetArg`, but will error out on failure.
|
||||||
getArg
|
getArg
|
||||||
:: forall m a
|
:: forall m a
|
||||||
. (FromGraphQL a)
|
. (FromGraphQL a)
|
||||||
=> (MonadIO m)
|
=> (MonadIO m)
|
||||||
=> Text
|
=> Text
|
||||||
|
-> [Text]
|
||||||
-> HashMap Text Value
|
-> HashMap Text Value
|
||||||
-> m a
|
-> m a
|
||||||
getArg name args = do
|
getArg name alts args = do
|
||||||
result <- tryGetArg name args
|
result <- tryGetArg name alts args
|
||||||
case result of
|
case result of
|
||||||
Just value -> pure value
|
Just value -> pure value
|
||||||
Nothing ->
|
Nothing ->
|
||||||
|
@ -487,16 +498,18 @@ getArg name args = do
|
||||||
"Argument " <> T.unpack name <> " not found"
|
"Argument " <> T.unpack name <> " not found"
|
||||||
|
|
||||||
|
|
||||||
|
-- Similar to `tryGetArg`, but will return a custom value on failure.
|
||||||
getArgWithDefault
|
getArgWithDefault
|
||||||
:: forall m a
|
:: forall m a
|
||||||
. (FromGraphQL a)
|
. (FromGraphQL a)
|
||||||
=> (MonadIO m)
|
=> (MonadIO m)
|
||||||
=> Text
|
=> Text
|
||||||
|
-> [Text]
|
||||||
-> HashMap Text Value
|
-> HashMap Text Value
|
||||||
-> a
|
-> a
|
||||||
-> m a
|
-> m a
|
||||||
getArgWithDefault name args def =
|
getArgWithDefault name alts args def =
|
||||||
tryGetArg name args <&> P.fromMaybe def
|
tryGetArg name alts args <&> P.fromMaybe def
|
||||||
|
|
||||||
|
|
||||||
executeUpdateMutation
|
executeUpdateMutation
|
||||||
|
@ -608,13 +621,8 @@ queryType connection accessMode dbId tables = do
|
||||||
|
|
||||||
rows :: [[SQLData]] <- case context.arguments of
|
rows :: [[SQLData]] <- case context.arguments of
|
||||||
Arguments args -> do
|
Arguments args -> do
|
||||||
filterElements <- case args & HashMap.lookup "filter" of
|
filterElements :: HashMap Text Value <-
|
||||||
Nothing -> pure []
|
getArgWithDefault "where" ["filter"] args HashMap.empty
|
||||||
Just colToFilter -> case colToFilter of
|
|
||||||
Object filterObj -> case HashMap.toList filterObj of
|
|
||||||
[] -> P.throwIO $ userError "Error: Filter must not be empty"
|
|
||||||
filterElements -> pure filterElements
|
|
||||||
_ -> pure []
|
|
||||||
|
|
||||||
orderElements :: [(Name, Value)] <-
|
orderElements :: [(Name, Value)] <-
|
||||||
case args & HashMap.lookup "order_by" of
|
case args & HashMap.lookup "order_by" of
|
||||||
|
@ -678,7 +686,7 @@ queryType connection accessMode dbId tables = do
|
||||||
[ "SELECT COUNT() FROM"
|
[ "SELECT COUNT() FROM"
|
||||||
, quoteKeyword table.name
|
, quoteKeyword table.name
|
||||||
, "\n"
|
, "\n"
|
||||||
, getWhereClause filterElements
|
, getWhereClause $ HashMap.toList filterElements
|
||||||
]
|
]
|
||||||
|
|
||||||
-- Will be equal `Just numRows` when the number of
|
-- Will be equal `Just numRows` when the number of
|
||||||
|
@ -720,7 +728,7 @@ queryType connection accessMode dbId tables = do
|
||||||
connection
|
connection
|
||||||
table.name
|
table.name
|
||||||
table.columns
|
table.columns
|
||||||
filterElements
|
(HashMap.toList filterElements)
|
||||||
orderElements
|
orderElements
|
||||||
paginationMb
|
paginationMb
|
||||||
|
|
||||||
|
@ -849,7 +857,7 @@ mutationType connection maxRowsPerTable accessMode dbId tables = do
|
||||||
-- [ { name: "John", email: "john@example.com" }
|
-- [ { name: "John", email: "john@example.com" }
|
||||||
-- , { name: "Eve", email: "eve@example.com" }
|
-- , { name: "Eve", email: "eve@example.com" }
|
||||||
-- ]
|
-- ]
|
||||||
values :: [HashMap Text Value] <- getArg "objects" argMap
|
values :: [HashMap Text Value] <- getArg "objects" [] argMap
|
||||||
let
|
let
|
||||||
-- All colums that are contained in the entries
|
-- All colums that are contained in the entries
|
||||||
containedColumns :: [Text]
|
containedColumns :: [Text]
|
||||||
|
@ -866,12 +874,12 @@ mutationType connection maxRowsPerTable accessMode dbId tables = do
|
||||||
<&> (\name -> ":" <> doubleXEncodeGql name)
|
<&> (\name -> ":" <> doubleXEncodeGql name)
|
||||||
|
|
||||||
onConflictArg :: [HashMap Text Value] <-
|
onConflictArg :: [HashMap Text Value] <-
|
||||||
getArgWithDefault "on_conflict" argMap []
|
getArgWithDefault "on_conflict" [] argMap []
|
||||||
|
|
||||||
onConflictClauses <- P.for onConflictArg $ \fields -> do
|
onConflictClauses <- P.for onConflictArg $ \fields -> do
|
||||||
let
|
let
|
||||||
getColumnList fieldName =
|
getColumnList fieldName =
|
||||||
getArgWithDefault fieldName fields []
|
getArgWithDefault fieldName [] fields []
|
||||||
<&> P.mapMaybe
|
<&> P.mapMaybe
|
||||||
( \case
|
( \case
|
||||||
Enum columnName -> Just columnName
|
Enum columnName -> Just columnName
|
||||||
|
@ -895,7 +903,7 @@ mutationType connection maxRowsPerTable accessMode dbId tables = do
|
||||||
<> " = :"
|
<> " = :"
|
||||||
<> doubleXEncodeGql column
|
<> doubleXEncodeGql column
|
||||||
|
|
||||||
filterElements <- getArgWithDefault "where" fields mempty
|
filterElements <- getArgWithDefault "where" ["filter"] fields mempty
|
||||||
|
|
||||||
pure $
|
pure $
|
||||||
"ON CONFLICT ("
|
"ON CONFLICT ("
|
||||||
|
@ -983,8 +991,8 @@ mutationType connection maxRowsPerTable accessMode dbId tables = do
|
||||||
context <- ask
|
context <- ask
|
||||||
let Arguments args = context.arguments
|
let Arguments args = context.arguments
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
filterObj <- getArg "filter" args
|
filterObj <- getArg "where" ["filter"] args
|
||||||
pairsToSet <- getArg "set" args
|
pairsToSet <- getArg "set" [] args
|
||||||
(numOfChanges, updatedRows) <- case HashMap.toList filterObj of
|
(numOfChanges, updatedRows) <- case HashMap.toList filterObj of
|
||||||
[] -> P.throwIO $ userError "Error: Filter must not be empty"
|
[] -> P.throwIO $ userError "Error: Filter must not be empty"
|
||||||
filterElements ->
|
filterElements ->
|
||||||
|
@ -1002,11 +1010,16 @@ mutationType connection maxRowsPerTable accessMode dbId tables = do
|
||||||
let Arguments args = context.arguments
|
let Arguments args = context.arguments
|
||||||
let filterElements =
|
let filterElements =
|
||||||
args
|
args
|
||||||
& HashMap.delete "set"
|
& HashMap.delete (encodeOutsidePKNames table "_set")
|
||||||
|
& HashMap.delete (encodeOutsidePKNames table "set")
|
||||||
& getByPKFilterElements
|
& getByPKFilterElements
|
||||||
|
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
pairsToSet <- getArg (encodeOutsidePKNames table "set") args
|
pairsToSet <-
|
||||||
|
getArg
|
||||||
|
(encodeOutsidePKNames table "_set")
|
||||||
|
[encodeOutsidePKNames table "set"]
|
||||||
|
args
|
||||||
(numOfChanges, updatedRows) <-
|
(numOfChanges, updatedRows) <-
|
||||||
executeUpdateMutation
|
executeUpdateMutation
|
||||||
connection
|
connection
|
||||||
|
@ -1023,7 +1036,7 @@ mutationType connection maxRowsPerTable accessMode dbId tables = do
|
||||||
let Arguments args = context.arguments
|
let Arguments args = context.arguments
|
||||||
|
|
||||||
liftIO $ do
|
liftIO $ do
|
||||||
filterElements <- getArg "filter" args
|
filterElements <- getArg "where" ["filter"] args
|
||||||
let sqlQuery =
|
let sqlQuery =
|
||||||
Query $
|
Query $
|
||||||
P.unlines
|
P.unlines
|
||||||
|
|
|
@ -114,22 +114,30 @@ filterType table = do
|
||||||
let colName = columnEntry.column_name_gql
|
let colName = columnEntry.column_name_gql
|
||||||
let typeName = columnTypeName columnEntry
|
let typeName = columnTypeName columnEntry
|
||||||
let type_ = columnType columnEntry
|
let type_ = columnType columnEntry
|
||||||
Type.inputValue colName $
|
|
||||||
Type.withDescription ("Compare to a(n) " <> typeName) $
|
let comparisonField name ty =
|
||||||
Type.inputObject
|
[ Type.deprecatedInputValue "Unify naming with Hasura" $
|
||||||
(typeName <> "Comparison")
|
Type.inputValue name ty
|
||||||
[ Type.inputValue "eq" type_
|
, Type.inputValue ("_" <> name) ty
|
||||||
, Type.inputValue "neq" type_
|
|
||||||
, Type.inputValue "gt" type_
|
|
||||||
, Type.inputValue "gte" type_
|
|
||||||
, Type.inputValue "lt" type_
|
|
||||||
, Type.inputValue "lte" type_
|
|
||||||
, Type.inputValue "like" type_
|
|
||||||
, Type.inputValue "ilike" type_
|
|
||||||
, Type.inputValue "in" $ Type.list type_
|
|
||||||
, Type.inputValue "nin" $ Type.list type_
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Type.inputValue colName
|
||||||
|
$ Type.withDescription ("Compare to a(n) " <> typeName)
|
||||||
|
$ Type.inputObject
|
||||||
|
(typeName <> "Comparison")
|
||||||
|
$ P.fold
|
||||||
|
[ comparisonField "eq" type_
|
||||||
|
, comparisonField "neq" type_
|
||||||
|
, comparisonField "gt" type_
|
||||||
|
, comparisonField "gte" type_
|
||||||
|
, comparisonField "lt" type_
|
||||||
|
, comparisonField "lte" type_
|
||||||
|
, comparisonField "like" type_
|
||||||
|
, comparisonField "ilike" type_
|
||||||
|
, comparisonField "in" $ Type.list type_
|
||||||
|
, comparisonField "nin" $ Type.list type_
|
||||||
|
]
|
||||||
|
|
||||||
Type.inputObject
|
Type.inputObject
|
||||||
(doubleXEncodeGql table.name <> "_filter")
|
(doubleXEncodeGql table.name <> "_filter")
|
||||||
fieldsWithComparisonExp
|
fieldsWithComparisonExp
|
||||||
|
@ -175,9 +183,12 @@ tableQueryField table =
|
||||||
(Type.nonNull $ Type.list $ Type.nonNull $ tableRowType table)
|
(Type.nonNull $ Type.list $ Type.nonNull $ tableRowType table)
|
||||||
& Type.fieldWithDescription ("Rows from the table \"" <> table.name <> "\"")
|
& Type.fieldWithDescription ("Rows from the table \"" <> table.name <> "\"")
|
||||||
& Type.withArguments
|
& Type.withArguments
|
||||||
[ Type.inputValue "filter" (filterType table)
|
( Type.inputValue "filter" (filterType table)
|
||||||
& Type.inputValueWithDescription "Filter to select specific rows"
|
& Type.inputValueWithDescription "Filter to select specific rows"
|
||||||
, Type.inputValue "order_by" (Type.list orderType)
|
& Type.renameInputValue "where" "Unify naming with Hasura"
|
||||||
|
)
|
||||||
|
& Type.withArguments
|
||||||
|
[ Type.inputValue "order_by" (Type.list orderType)
|
||||||
& Type.inputValueWithDescription "Columns used to sort the data"
|
& Type.inputValueWithDescription "Columns used to sort the data"
|
||||||
, Type.inputValue "limit" Type.typeInt
|
, Type.inputValue "limit" Type.typeInt
|
||||||
& Type.inputValueWithDescription "Limit the number of returned rows"
|
& Type.inputValueWithDescription "Limit the number of returned rows"
|
||||||
|
@ -359,15 +370,19 @@ tableUpdateField accessMode table = do
|
||||||
& Type.fieldWithDescription
|
& Type.fieldWithDescription
|
||||||
("Update rows in table \"" <> table.name <> "\"")
|
("Update rows in table \"" <> table.name <> "\"")
|
||||||
& Type.withArguments
|
& Type.withArguments
|
||||||
[ Type.inputValue
|
( Type.inputValue
|
||||||
"set"
|
"set"
|
||||||
(Type.nonNull $ tableSetInput table)
|
(Type.nonNull $ tableSetInput table)
|
||||||
& Type.inputValueWithDescription "Fields to be updated"
|
& Type.inputValueWithDescription "Fields to be updated"
|
||||||
, Type.inputValue
|
& Type.renameInputValue "_set" "Unify naming with Hasura"
|
||||||
|
)
|
||||||
|
& Type.withArguments
|
||||||
|
( Type.inputValue
|
||||||
"filter"
|
"filter"
|
||||||
(Type.nonNull $ filterType table)
|
(Type.nonNull $ filterType table)
|
||||||
& Type.inputValueWithDescription "Filter to select rows to be updated"
|
& Type.inputValueWithDescription "Filter to select rows to be updated"
|
||||||
]
|
& Type.renameInputValue "where" "Unify naming with Hasura"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
tableUpdateFieldByPk
|
tableUpdateFieldByPk
|
||||||
|
@ -409,11 +424,12 @@ tableDeleteField accessMode table = do
|
||||||
& Type.fieldWithDescription
|
& Type.fieldWithDescription
|
||||||
("Delete rows in table \"" <> table.name <> "\"")
|
("Delete rows in table \"" <> table.name <> "\"")
|
||||||
& Type.withArguments
|
& Type.withArguments
|
||||||
[ Type.inputValue
|
( Type.inputValue
|
||||||
"filter"
|
"filter"
|
||||||
(Type.nonNull $ filterType table)
|
(Type.nonNull $ filterType table)
|
||||||
& Type.inputValueWithDescription "Filter to select rows to be deleted"
|
& Type.inputValueWithDescription "Filter to select rows to be deleted"
|
||||||
]
|
& Type.renameInputValue "where" "Unify naming with Hasura"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
tableDeleteFieldByPK
|
tableDeleteFieldByPK
|
||||||
|
|
|
@ -30,6 +30,7 @@ import Control.Exception qualified as Exception
|
||||||
import Data.HashMap.Strict qualified as HashMap
|
import Data.HashMap.Strict qualified as HashMap
|
||||||
import Data.Text qualified as T
|
import Data.Text qualified as T
|
||||||
import GHC.IO.Exception (userError)
|
import GHC.IO.Exception (userError)
|
||||||
|
import Language.GraphQL.Class (ToGraphQL (toGraphQL))
|
||||||
import Language.GraphQL.Error (ResolverException (ResolverException))
|
import Language.GraphQL.Error (ResolverException (ResolverException))
|
||||||
import Language.GraphQL.Type qualified as Type
|
import Language.GraphQL.Type qualified as Type
|
||||||
import Language.GraphQL.Type.In qualified as In
|
import Language.GraphQL.Type.In qualified as In
|
||||||
|
@ -120,7 +121,7 @@ makeType =
|
||||||
$ HashMap.fromList
|
$ HashMap.fromList
|
||||||
$ ("__typename", typenameResolver) : resolvers
|
$ ("__typename", typenameResolver) : resolvers
|
||||||
_ -> do
|
_ -> do
|
||||||
Left $ "invalid type in out position: " <> show ty.kind
|
Left $ "invalid type in out position: " <> show (toGraphQL ty)
|
||||||
|
|
||||||
-- Creates a field which looks up it's value in the object returned by the
|
-- Creates a field which looks up it's value in the object returned by the
|
||||||
-- parent resolver.
|
-- parent resolver.
|
||||||
|
@ -152,7 +153,7 @@ makeType =
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
|
|
||||||
case HashMap.lookup field.name obj of
|
case HashMap.lookup field.name obj of
|
||||||
Just value -> pure value
|
Just value -> field.customResolver value
|
||||||
Nothing -> defaultValue
|
Nothing -> defaultValue
|
||||||
_ -> defaultValue
|
_ -> defaultValue
|
||||||
in
|
in
|
||||||
|
@ -219,4 +220,4 @@ makeInType ty = do
|
||||||
ty.description
|
ty.description
|
||||||
$ HashMap.fromList gqlFields
|
$ HashMap.fromList gqlFields
|
||||||
_ -> do
|
_ -> do
|
||||||
Left $ "invalid type in input position: " <> show ty.kind
|
Left $ "invalid type in input position: " <> show (toGraphQL ty)
|
||||||
|
|
|
@ -16,9 +16,12 @@ module AirGQL.Introspection.Types (
|
||||||
enumValueWithDescription,
|
enumValueWithDescription,
|
||||||
field,
|
field,
|
||||||
fieldWithDescription,
|
fieldWithDescription,
|
||||||
|
deprecatedField,
|
||||||
inputObject,
|
inputObject,
|
||||||
inputValue,
|
inputValue,
|
||||||
inputValueWithDescription,
|
inputValueWithDescription,
|
||||||
|
deprecatedInputValue,
|
||||||
|
renameInputValue,
|
||||||
list,
|
list,
|
||||||
nonNull,
|
nonNull,
|
||||||
object,
|
object,
|
||||||
|
@ -40,13 +43,15 @@ module AirGQL.Introspection.Types (
|
||||||
import Protolude (
|
import Protolude (
|
||||||
Bool (False, True),
|
Bool (False, True),
|
||||||
Generic,
|
Generic,
|
||||||
|
IO,
|
||||||
Maybe (Just, Nothing),
|
Maybe (Just, Nothing),
|
||||||
|
MonadReader (ask),
|
||||||
MonadState (get, put),
|
MonadState (get, put),
|
||||||
Monoid (mempty),
|
Monoid (mempty),
|
||||||
Show,
|
|
||||||
State,
|
State,
|
||||||
Text,
|
Text,
|
||||||
execState,
|
execState,
|
||||||
|
filter,
|
||||||
for_,
|
for_,
|
||||||
not,
|
not,
|
||||||
pure,
|
pure,
|
||||||
|
@ -54,8 +59,10 @@ import Protolude (
|
||||||
when,
|
when,
|
||||||
($),
|
($),
|
||||||
(&),
|
(&),
|
||||||
|
(/=),
|
||||||
(<$>),
|
(<$>),
|
||||||
(<>),
|
(<>),
|
||||||
|
(==),
|
||||||
)
|
)
|
||||||
|
|
||||||
import Data.HashMap.Strict (HashMap)
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
@ -71,7 +78,7 @@ data Schema = Schema
|
||||||
, mutationType :: Maybe IntrospectionType
|
, mutationType :: Maybe IntrospectionType
|
||||||
, directives :: [Directive]
|
, directives :: [Directive]
|
||||||
}
|
}
|
||||||
deriving (Show, Generic)
|
deriving (Generic)
|
||||||
|
|
||||||
|
|
||||||
instance ToGraphQL Schema where
|
instance ToGraphQL Schema where
|
||||||
|
@ -98,14 +105,14 @@ data TypeKind
|
||||||
| InputObject Name [InputValue]
|
| InputObject Name [InputValue]
|
||||||
| List IntrospectionType
|
| List IntrospectionType
|
||||||
| NonNull IntrospectionType
|
| NonNull IntrospectionType
|
||||||
deriving (Show, Generic)
|
deriving (Generic)
|
||||||
|
|
||||||
|
|
||||||
data IntrospectionType = IType
|
data IntrospectionType = IType
|
||||||
{ kind :: TypeKind
|
{ kind :: TypeKind
|
||||||
, description :: Maybe Text
|
, description :: Maybe Text
|
||||||
}
|
}
|
||||||
deriving (Show, Generic)
|
deriving (Generic)
|
||||||
|
|
||||||
|
|
||||||
instance ToGraphQL IntrospectionType where
|
instance ToGraphQL IntrospectionType where
|
||||||
|
@ -214,8 +221,11 @@ data Field = Field
|
||||||
, type_ :: IntrospectionType
|
, type_ :: IntrospectionType
|
||||||
, isDeprecated :: Bool
|
, isDeprecated :: Bool
|
||||||
, deprecationReason :: Maybe Text
|
, deprecationReason :: Maybe Text
|
||||||
|
, -- Allows injecting custom logic into the resulting resolver. The function
|
||||||
|
-- is given the value of the field in the parent object.
|
||||||
|
customResolver :: Value.Value -> Value.Resolve IO
|
||||||
}
|
}
|
||||||
deriving (Show, Generic)
|
deriving (Generic)
|
||||||
|
|
||||||
|
|
||||||
instance ToGraphQL Field where
|
instance ToGraphQL Field where
|
||||||
|
@ -231,14 +241,6 @@ instance ToGraphQL Field where
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
fieldWithDescription :: Text -> Field -> Field
|
|
||||||
fieldWithDescription newDesc (Field{..}) =
|
|
||||||
Field
|
|
||||||
{ description = Just newDesc
|
|
||||||
, ..
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
field :: Text -> IntrospectionType -> Field
|
field :: Text -> IntrospectionType -> Field
|
||||||
field fieldName fieldType =
|
field fieldName fieldType =
|
||||||
Field
|
Field
|
||||||
|
@ -248,6 +250,24 @@ field fieldName fieldType =
|
||||||
, type_ = fieldType
|
, type_ = fieldType
|
||||||
, isDeprecated = False
|
, isDeprecated = False
|
||||||
, deprecationReason = Nothing
|
, deprecationReason = Nothing
|
||||||
|
, customResolver = pure
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fieldWithDescription :: Text -> Field -> Field
|
||||||
|
fieldWithDescription newDesc (Field{..}) =
|
||||||
|
Field
|
||||||
|
{ description = Just newDesc
|
||||||
|
, ..
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
deprecatedField :: Text -> Field -> Field
|
||||||
|
deprecatedField reason (Field{..}) =
|
||||||
|
Field
|
||||||
|
{ isDeprecated = True
|
||||||
|
, deprecationReason = Just reason
|
||||||
|
, ..
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -260,8 +280,10 @@ data InputValue = InputValue
|
||||||
, description :: Maybe Text
|
, description :: Maybe Text
|
||||||
, type_ :: IntrospectionType
|
, type_ :: IntrospectionType
|
||||||
, defaultValue :: Maybe Value.Value
|
, defaultValue :: Maybe Value.Value
|
||||||
|
, isDeprecated :: Bool
|
||||||
|
, deprecationReason :: Maybe Text
|
||||||
}
|
}
|
||||||
deriving (Show, Generic)
|
deriving (Generic)
|
||||||
|
|
||||||
|
|
||||||
instance ToGraphQL InputValue where
|
instance ToGraphQL InputValue where
|
||||||
|
@ -278,6 +300,8 @@ instance ToGraphQL InputValue where
|
||||||
Nothing -> Value.Null
|
Nothing -> Value.Null
|
||||||
Just s -> Value.String $ show s
|
Just s -> Value.String $ show s
|
||||||
)
|
)
|
||||||
|
, ("isDeprecated", toGraphQL value.isDeprecated)
|
||||||
|
, ("deprecationReason", toGraphQL value.deprecationReason)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -288,6 +312,8 @@ inputValue fieldName fieldType =
|
||||||
, description = Nothing
|
, description = Nothing
|
||||||
, type_ = fieldType
|
, type_ = fieldType
|
||||||
, defaultValue = Nothing
|
, defaultValue = Nothing
|
||||||
|
, isDeprecated = False
|
||||||
|
, deprecationReason = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -307,13 +333,37 @@ withDefaultValue newValue (InputValue{..}) =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
deprecatedInputValue :: Text -> InputValue -> InputValue
|
||||||
|
deprecatedInputValue reason (InputValue{..}) =
|
||||||
|
InputValue
|
||||||
|
{ isDeprecated = True
|
||||||
|
, deprecationReason = Just reason
|
||||||
|
, ..
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-- Rename an input value, deprecating the old version.
|
||||||
|
renameInputValue :: Text -> Text -> InputValue -> [InputValue]
|
||||||
|
renameInputValue to reason (InputValue{..}) =
|
||||||
|
[ InputValue
|
||||||
|
{ name = to
|
||||||
|
, ..
|
||||||
|
}
|
||||||
|
, InputValue
|
||||||
|
{ isDeprecated = True
|
||||||
|
, deprecationReason = Just reason
|
||||||
|
, ..
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
data EnumValue = EnumValue
|
data EnumValue = EnumValue
|
||||||
{ name :: Text
|
{ name :: Text
|
||||||
, description :: Maybe Text
|
, description :: Maybe Text
|
||||||
, isDeprecated :: Bool
|
, isDeprecated :: Bool
|
||||||
, deprecationReason :: Maybe Text
|
, deprecationReason :: Maybe Text
|
||||||
}
|
}
|
||||||
deriving (Show, Generic)
|
deriving (Generic)
|
||||||
|
|
||||||
|
|
||||||
instance ToGraphQL EnumValue where
|
instance ToGraphQL EnumValue where
|
||||||
|
@ -358,7 +408,7 @@ data Directive = Directive
|
||||||
, args :: [InputValue]
|
, args :: [InputValue]
|
||||||
, isRepeatable :: Bool
|
, isRepeatable :: Bool
|
||||||
}
|
}
|
||||||
deriving (Generic, Show)
|
deriving (Generic)
|
||||||
|
|
||||||
|
|
||||||
instance ToGraphQL Directive where
|
instance ToGraphQL Directive where
|
||||||
|
@ -486,7 +536,8 @@ typeField =
|
||||||
"__Field"
|
"__Field"
|
||||||
[ field "name" $ nonNull typeString
|
[ field "name" $ nonNull typeString
|
||||||
, field "description" typeString
|
, field "description" typeString
|
||||||
, field "args" $ nonNull $ list $ nonNull typeInputValue
|
, field "args" (nonNull $ list $ nonNull typeInputValue)
|
||||||
|
& typeWithDeprecatedFilter
|
||||||
, field "type" $ nonNull typeIntrospectionType
|
, field "type" $ nonNull typeIntrospectionType
|
||||||
, field "isDeprecated" $ nonNull typeBool
|
, field "isDeprecated" $ nonNull typeBool
|
||||||
, field "deprecationReason" typeString
|
, field "deprecationReason" typeString
|
||||||
|
@ -507,6 +558,8 @@ typeInputValue =
|
||||||
& fieldWithDescription
|
& fieldWithDescription
|
||||||
"A GraphQL-formatted string representing \
|
"A GraphQL-formatted string representing \
|
||||||
\the default value for this input value."
|
\the default value for this input value."
|
||||||
|
, field "isDeprecated" $ nonNull typeBool
|
||||||
|
, field "deprecationReason" typeString
|
||||||
]
|
]
|
||||||
& withDescription
|
& withDescription
|
||||||
"Arguments provided to Fields or Directives and the input \
|
"Arguments provided to Fields or Directives and the input \
|
||||||
|
@ -574,16 +627,11 @@ typeIntrospectionType =
|
||||||
, field "interfaces" $ list $ nonNull typeIntrospectionType
|
, field "interfaces" $ list $ nonNull typeIntrospectionType
|
||||||
, field "possibleTypes" $ list $ nonNull typeIntrospectionType
|
, field "possibleTypes" $ list $ nonNull typeIntrospectionType
|
||||||
, field "fields" (list $ nonNull typeField)
|
, field "fields" (list $ nonNull typeField)
|
||||||
& withArguments
|
& typeWithDeprecatedFilter
|
||||||
[ inputValue "includeDeprecated" typeBool
|
|
||||||
& withDefaultValue (toGraphQL False)
|
|
||||||
]
|
|
||||||
, field "enumValues" (list $ nonNull typeEnumValue)
|
, field "enumValues" (list $ nonNull typeEnumValue)
|
||||||
& withArguments
|
& typeWithDeprecatedFilter
|
||||||
[ inputValue "includeDeprecated" typeBool
|
, field "inputFields" (list $ nonNull typeInputValue)
|
||||||
& withDefaultValue (toGraphQL False)
|
& typeWithDeprecatedFilter
|
||||||
]
|
|
||||||
, field "inputFields" $ list $ nonNull typeInputValue
|
|
||||||
, field "ofType" typeIntrospectionType
|
, field "ofType" typeIntrospectionType
|
||||||
]
|
]
|
||||||
& withDescription
|
& withDescription
|
||||||
|
@ -636,7 +684,11 @@ typeDirective =
|
||||||
"__Directive"
|
"__Directive"
|
||||||
[ field "name" $ nonNull typeString
|
[ field "name" $ nonNull typeString
|
||||||
, field "description" typeString
|
, field "description" typeString
|
||||||
, field "args" $ nonNull $ list $ nonNull typeInputValue
|
, field "args" (nonNull $ list $ nonNull typeInputValue)
|
||||||
|
& withArguments
|
||||||
|
[ inputValue "includeDeprecated" typeBool
|
||||||
|
& withDefaultValue (toGraphQL False)
|
||||||
|
]
|
||||||
, field "isRepeatable" $ nonNull typeBool
|
, field "isRepeatable" $ nonNull typeBool
|
||||||
, field "locations" $ nonNull $ list $ nonNull typeDirectiveLocation
|
, field "locations" $ nonNull $ list $ nonNull typeDirectiveLocation
|
||||||
]
|
]
|
||||||
|
@ -700,3 +752,38 @@ typeDirectiveLocation =
|
||||||
& withDescription
|
& withDescription
|
||||||
"A Directive can be adjacent to many parts of the GraphQL language, \
|
"A Directive can be adjacent to many parts of the GraphQL language, \
|
||||||
\a __DirectiveLocation describes one such possible adjacencies."
|
\a __DirectiveLocation describes one such possible adjacencies."
|
||||||
|
|
||||||
|
|
||||||
|
-- Internal helper which adds custom logic to a resolver for handling the
|
||||||
|
-- `includeDeprecated` filter.
|
||||||
|
typeWithDeprecatedFilter :: Field -> Field
|
||||||
|
typeWithDeprecatedFilter Field{..} =
|
||||||
|
Field
|
||||||
|
{ args =
|
||||||
|
args
|
||||||
|
<> [ inputValue "includeDeprecated" typeBool
|
||||||
|
& withDefaultValue (toGraphQL False)
|
||||||
|
]
|
||||||
|
, customResolver = \case
|
||||||
|
Value.List values -> do
|
||||||
|
context <- ask
|
||||||
|
let
|
||||||
|
Value.Arguments gqlArgs = context.arguments
|
||||||
|
includeDeprecated = HashMap.lookup "includeDeprecated" gqlArgs
|
||||||
|
filtered =
|
||||||
|
if includeDeprecated == Just (Value.Boolean True)
|
||||||
|
then values
|
||||||
|
else
|
||||||
|
filter
|
||||||
|
( \case
|
||||||
|
Value.Object fields ->
|
||||||
|
HashMap.lookup "isDeprecated" fields
|
||||||
|
/= Just (Value.Boolean True)
|
||||||
|
_ -> True
|
||||||
|
)
|
||||||
|
values
|
||||||
|
|
||||||
|
customResolver $ Value.List filtered
|
||||||
|
o -> customResolver o
|
||||||
|
, ..
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue