1
Fork 0
mirror of https://github.com/Airsequel/AirGQL.git synced 2025-07-10 00:44:55 +03:00
airgql/source/AirGQL/Introspection.hs

2540 lines
105 KiB
Haskell

module AirGQL.Introspection (
getSchemaResolver,
typeNameResolver,
createType,
)
where
import Protolude (
Applicative (pure),
Bool (False, True),
Eq ((/=)),
Foldable (null),
IO,
Int,
IsString,
Maybe (Just, Nothing),
MonadReader (ask),
Monoid (mempty),
Num ((+)),
Ord ((<)),
Semigroup ((<>)),
Text,
concat,
filter,
forM,
fromMaybe,
not,
($),
(&),
(<&>),
(>>=),
)
import Data.HashMap.Strict as HashMap (
HashMap,
empty,
fromList,
lookup,
singleton,
)
import Database.SQLite.Simple (Connection)
import Language.GraphQL.Type (
Value (Boolean, List, Null, Object, String),
boolean,
string,
)
import Language.GraphQL.Type.In as In (Type (NamedScalarType))
import Language.GraphQL.Type.Out as Out (
Context (values),
Field (Field),
Resolver (ValueResolver),
Type (
ListType,
NamedObjectType,
NamedScalarType,
NonNullListType,
NonNullObjectType,
NonNullScalarType
),
)
import AirGQL.GQLWrapper (
InArgument (InArgument, argDescMb, argType, valueMb),
OutField (OutField, arguments, descriptionMb, fieldType),
inArgumentToArgument,
outFieldToField,
)
import AirGQL.Lib (
AccessMode (ReadAndWrite, ReadOnly, WriteOnly),
ColumnEntry,
GqlTypeName (full),
TableEntryRaw (name),
column_name_gql,
datatype_gql,
getColumns,
notnull,
select_options,
)
import AirGQL.Types.OutObjectType (
OutObjectType (OutObjectType, descriptionMb, fields, interfaceTypes, name),
outObjectTypeToObjectType,
)
import DoubleXEncoding (doubleXEncodeGql)
emptyType :: Value
emptyType =
Object $ HashMap.singleton "kind" "OBJECT"
intType :: Value
intType =
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "SCALAR")
, ("name", "Int")
,
( "description"
, "The `Int` scalar type represents \
\non-fractional signed whole numeric values. \
\Int can represent values between -(2^31) and 2^31 - 1."
)
]
floatType :: Value
floatType =
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "SCALAR")
, ("name", "Float")
,
( "description"
, "Signed double-precision floating-point value."
)
]
stringType :: Value
stringType =
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "SCALAR")
, ("name", "String")
,
( "description"
, "The `String` scalar type represents textual data, \
\represented as UTF-8 character sequences. \
\The String type is most often used by GraphQL \
\to represent free-form human-readable text."
)
]
booleanType :: Value
booleanType =
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "SCALAR")
, ("name", "Boolean")
,
( "description"
, "The `Boolean` scalar type represents `true` or `false`."
)
]
nonNullString :: Out.Field IO
nonNullString =
outFieldToField $
OutField
{ descriptionMb = Just "nonNullString description"
, fieldType = Out.NonNullScalarType string
, arguments = HashMap.empty
}
nullableString :: Out.Field IO
nullableString =
Out.Field
(Just "nullableString")
(Out.NamedScalarType string)
HashMap.empty
nonNullBoolean :: Out.Field IO
nonNullBoolean =
outFieldToField $
OutField
{ descriptionMb = Just "nonNullBoolean description"
, fieldType = Out.NonNullScalarType boolean
, arguments = HashMap.empty
}
getTypeTuple :: (IsString a) => Value -> Value -> (a, Value)
getTypeTuple theKind theType =
( "type"
, Object $
HashMap.fromList
[ ("kind", theKind)
, ("name", theType)
]
)
nonNullType :: Value -> Value
nonNullType inner =
Object $
HashMap.fromList
[ ("kind", "NON_NULL")
, ("ofType", inner)
]
listType :: Value -> Value
listType inner =
Object $
HashMap.fromList
[ ("kind", "LIST")
, ("ofType", inner)
]
createType :: Text -> Text -> [Value] -> [Text] -> Text -> Value
createType rootName description args nestedTypes name =
let
createChildType :: [Text] -> Text -> Value
createChildType nestedChildTypes childName =
case nestedChildTypes of
[] -> Null
(childHeadKind : childRestKinds) ->
if not $ null childRestKinds
then
Object $
HashMap.fromList
[ ("kind", String childHeadKind)
, ("ofType", createChildType childRestKinds childName)
]
else
Object $
HashMap.fromList
[ ("kind", String childHeadKind)
, ("name", String name)
]
in
case nestedTypes of
[] -> Null
kinds ->
Object $
HashMap.fromList
( [ ("name", String rootName)
, ("description", String description)
, ("type", createChildType kinds name)
]
<> if null args then [] else [("args", List args)]
)
createField :: Text -> Maybe Text -> Value -> Value
createField name descriptionMb type_ =
Object $
HashMap.fromList
[ ("name", String name)
, ("type", type_)
]
<> case descriptionMb of
Nothing -> mempty
Just description ->
HashMap.singleton
"description"
(String description)
nameField :: Value
nameField =
Object $
HashMap.fromList
[ ("name", "name")
,
( "type"
, nonNullType $
Object $
HashMap.fromList
[ ("kind", "SCALAR")
, ("name", "String")
]
)
]
descriptionField :: Value
descriptionField =
Object $
HashMap.fromList
[ ("name", "description")
, getTypeTuple "SCALAR" "String"
]
argsFieldValue :: Value
argsFieldValue =
Object $
HashMap.fromList
[ ("name", "args")
,
( "type"
, nonNullType $
listType $
nonNullType $
Object $
HashMap.fromList
[ ("kind", "OBJECT")
, ("name", "__InputValue")
]
)
]
locationsFieldValue :: Value
locationsFieldValue =
Object $
HashMap.fromList
[ ("name", "locations")
,
( "type"
, nonNullType $
listType $
nonNullType $
Object $
HashMap.fromList
[ ("kind", "ENUM")
, ("name", "__DirectiveLocation")
]
)
]
typeFieldValue :: Value
typeFieldValue =
Object $
HashMap.fromList
[ ("name", "type")
,
( "type"
, nonNullType $
Object $
HashMap.fromList
[ ("kind", "OBJECT")
, ("name", "__Type")
]
)
]
isDeprecatedFieldValue :: Value
isDeprecatedFieldValue =
Object $
HashMap.fromList
[ ("name", "isDeprecated")
,
( "type"
, nonNullType $
Object $
HashMap.fromList
[ ("kind", "SCALAR")
, ("name", "Boolean")
]
)
]
typeType :: Int -> Out.Type IO
typeType level =
Out.NamedObjectType $
outObjectTypeToObjectType $
OutObjectType
{ name = "__Type"
, descriptionMb = Just "__Type description"
, interfaceTypes = []
, fields =
HashMap.fromList $
[
( "__typename"
, ValueResolver nonNullString $ pure "__Type"
)
,
( "kind"
, ValueResolver nonNullString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe "ERROR: kind" $
HashMap.lookup "kind" obj
_ -> pure "ERROR: kind"
)
,
( "name"
, ValueResolver nullableString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "name" obj
_ -> pure Null
)
,
( "description"
, ValueResolver nullableString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "description" obj
_ -> pure Null
)
,
( "fields"
, ValueResolver fieldsField $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "fields" obj
_ -> pure Null
)
,
( "possibleTypes"
, ValueResolver typesField $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "possibleTypes" obj
_ -> pure Null
)
,
( "interfaces"
, ValueResolver typesField $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe (List []) $
HashMap.lookup "interfaces" obj
_ -> pure $ List []
)
,
( "inputFields"
, ValueResolver inputsFieldsField $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "inputFields" obj
_ -> pure Null
)
,
( "enumValues"
, ValueResolver enumValuesField $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "enumValues" obj
_ -> pure Null
)
]
<> ( if level < 7
then
[
( "ofType"
, ValueResolver (typeField $ level + 1) $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "ofType" obj
_ -> pure Null
)
]
else []
)
}
typeField :: Int -> Field IO
typeField level =
outFieldToField $
OutField
{ descriptionMb = Just "typeField description"
, fieldType = typeType level
, arguments = HashMap.empty
}
typesField :: Field IO
typesField =
outFieldToField $
OutField
{ descriptionMb = Just "typesField description"
, fieldType = Out.ListType $ typeType 0
, arguments = HashMap.empty
}
inputValueType :: Out.Type IO
inputValueType =
Out.NonNullObjectType $
outObjectTypeToObjectType $
OutObjectType
{ name = "__InputValue"
, descriptionMb = Just "__InputValue description"
, interfaceTypes = []
, fields =
HashMap.fromList
[
( "__typename"
, ValueResolver nonNullString $ pure "__InputValue"
)
,
( "name"
, ValueResolver nonNullString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe "ERROR: name" $
HashMap.lookup "name" obj
_ -> pure "ERROR: name"
)
,
( "description"
, ValueResolver nullableString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "description" obj
_ -> pure Null
)
,
( "defaultValue"
, ValueResolver nullableString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "defaultValue" obj
_ -> pure Null
)
,
( "type"
, ValueResolver (typeField 0) $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe emptyType $
HashMap.lookup "type" obj
_ -> pure emptyType
)
]
}
argsField :: Field IO
argsField =
outFieldToField $
OutField
{ descriptionMb = Just "argsField description"
, fieldType = Out.NonNullListType inputValueType
, arguments = HashMap.empty
}
inputsFieldsField :: Field IO
inputsFieldsField =
outFieldToField $
OutField
{ descriptionMb = Just "inputsFieldsField description"
, fieldType = Out.ListType inputValueType
, arguments = HashMap.empty
}
enumValuesType :: Out.Type IO
enumValuesType =
Out.ListType $
Out.NonNullObjectType $
outObjectTypeToObjectType $
OutObjectType
{ name = "__EnumValue"
, descriptionMb = Just "__EnumValue description"
, interfaceTypes = []
, fields =
HashMap.fromList
[
( "__typename"
, ValueResolver nonNullString $ pure "__EnumValue"
)
,
( "name"
, ValueResolver nonNullString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe "ERROR: name" $
HashMap.lookup "name" obj
_ -> pure "ERROR: name"
)
,
( "description"
, ValueResolver nullableString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "description" obj
_ -> pure Null
)
,
( "isDeprecated"
, ValueResolver nonNullBoolean $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe (Boolean False) $
HashMap.lookup "isDeprecated" obj
_ -> pure $ Boolean False
)
,
( "deprecationReason"
, ValueResolver nullableString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "deprecationReason" obj
_ -> pure Null
)
]
}
enumValuesField :: Field IO
enumValuesField =
outFieldToField $
OutField
{ descriptionMb = Just "enumValuesField description"
, fieldType = enumValuesType
, arguments =
HashMap.fromList
[
( "includeDeprecated"
, inArgumentToArgument $
InArgument
{ argDescMb = Just "includeDeprecated description"
, argType = In.NamedScalarType boolean
, valueMb = Just $ Boolean True
}
)
]
}
queryTypeType :: Field IO
queryTypeType =
outFieldToField $
OutField
{ descriptionMb = Just "Provides the queryType"
, fieldType = typeType 0
, arguments = HashMap.empty
}
mutationTypeType :: Field IO
mutationTypeType =
outFieldToField $
OutField
{ descriptionMb = Just "Provides the mutationType"
, fieldType = typeType 0
, arguments = HashMap.empty
}
subscriptionTypeType :: Field IO
subscriptionTypeType =
outFieldToField $
OutField
{ descriptionMb = Just "Provides the subscriptionType"
, fieldType = typeType 0
, arguments = HashMap.empty
}
fieldsTypeOutput :: Out.Type IO
fieldsTypeOutput =
Out.ListType $
Out.NonNullObjectType $
outObjectTypeToObjectType $
OutObjectType
{ name = "__Field"
, descriptionMb = Just "__Field description"
, interfaceTypes = []
, fields =
HashMap.fromList
[
( "__typename"
, ValueResolver nonNullString $ pure "__Field"
)
,
( "name"
, ValueResolver nonNullString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe "ERROR: name" $
HashMap.lookup "name" obj
_ -> pure "ERROR: name"
)
,
( "description"
, ValueResolver nullableString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "description" obj
_ -> pure Null
)
,
( "args"
, ValueResolver argsField $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe (List []) $
HashMap.lookup "args" obj
_ -> pure $ List []
)
,
( "type"
, ValueResolver (typeField 0) $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe emptyType $
HashMap.lookup "type" obj
_ -> pure emptyType
)
,
( "isDeprecated"
, ValueResolver nonNullBoolean $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe (Boolean False) $
HashMap.lookup "isDeprecated" obj
_ -> pure $ Boolean False
)
,
( "deprecationReason"
, ValueResolver nullableString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "deprecationReason" obj
_ -> pure Null
)
]
}
fieldsField :: Field IO
fieldsField =
outFieldToField $
OutField
{ descriptionMb = Just "The fields type"
, fieldType = fieldsTypeOutput
, arguments =
HashMap.fromList
[
( "includeDeprecated"
, inArgumentToArgument $
InArgument
{ argDescMb = Just "includeDeprecated description"
, argType = In.NamedScalarType boolean
, valueMb = Just $ Boolean True
}
)
]
}
directivesType :: Field IO
directivesType =
let
directivesTypeOutput :: Out.Type IO
directivesTypeOutput =
Out.ListType $
Out.NonNullObjectType $
outObjectTypeToObjectType $
OutObjectType
{ name = "__Directive"
, descriptionMb = Just "__Directive description"
, interfaceTypes = []
, fields =
HashMap.fromList
[
( "__typename"
, ValueResolver nullableString $ pure "__Directive"
)
,
( "name"
, ValueResolver nonNullString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe "ERROR: name" $
HashMap.lookup "name" obj
_ -> pure "ERROR: name"
)
,
( "description"
, ValueResolver nullableString $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe Null $
HashMap.lookup "description" obj
_ -> pure Null
)
,
( "locations"
, let
locationsTypeName :: Field m
locationsTypeName =
Out.Field
(Just "locationsTypeName name")
(Out.ListType $ Out.NonNullScalarType string)
HashMap.empty
in
ValueResolver locationsTypeName $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe (List []) $
HashMap.lookup "locations" obj
_ -> pure $ List []
)
,
( "args"
, ValueResolver argsField $ do
context <- ask
case context & Out.values of
Object obj ->
pure $
fromMaybe (List []) $
HashMap.lookup "args" obj
_ -> pure $ List []
)
]
}
in
outFieldToField $
OutField
{ descriptionMb = Just "Provides the directivesType"
, fieldType = directivesTypeOutput
, arguments = HashMap.empty
}
filterType :: Bool -> Text -> Value
filterType isRequired tableName =
let
filterObj =
Object $
HashMap.fromList
[ ("kind", "INPUT_OBJECT")
, ("name", String $ doubleXEncodeGql tableName <> "_filter")
,
( "description"
, "Select rows matching the provided filter object"
)
]
in
if isRequired
then nonNullType filterObj
else filterObj
getFieldsForQuery :: Text -> Value
getFieldsForQuery tableName =
createType
(doubleXEncodeGql tableName)
("Rows from the table \"" <> tableName <> "\"")
[ Object $
HashMap.fromList
[ ("name", "filter")
, ("description", "Filter to select specific rows")
, ("type", filterType False tableName)
]
, Object $
HashMap.fromList
[ ("name", "order_by")
, ("description", "Columns used to sort the data")
,
( "type"
, listType $
Object $
HashMap.fromList
[ ("kind", "INPUT_OBJECT")
,
( "name"
, String $
doubleXEncodeGql tableName
<> "_order_by"
)
]
)
]
, Object $
HashMap.fromList
[ ("name", "limit")
, ("description", "Limit the number of returned rows")
, ("type", intType)
]
, Object $
HashMap.fromList
[ ("name", "offset")
, ("description", "The index to start returning rows from")
, ("type", intType)
]
]
["NON_NULL", "LIST", "NON_NULL", "OBJECT"]
(doubleXEncodeGql tableName <> "_row")
getFieldsForMutation :: Text -> [Value]
getFieldsForMutation tableName =
[ Object $
HashMap.fromList
[ ("name", String $ "insert_" <> doubleXEncodeGql tableName)
,
( "description"
, String $
"Insert new rows in table \"" <> tableName <> "\""
)
,
( "args"
, List
[ createField
"objects"
(Just "Rows to be inserted")
$ nonNullType
$ listType
$ nonNullType
$ Object
$ HashMap.fromList
[ ("kind", "INPUT_OBJECT")
,
( "name"
, String $
doubleXEncodeGql
tableName
<> "_insert_input"
)
]
, createField
"on_conflict"
(Just "Specifies how to handle broken UNIQUE constraints")
$ listType
$ nonNullType
$ Object
$ HashMap.fromList
[ ("kind", "INPUT_OBJECT")
,
( "name"
, String $
doubleXEncodeGql
tableName
<> "_upsert_on_conflict"
)
]
]
)
,
( "type"
, nonNullType $
Object $
HashMap.fromList
[ ("kind", "OBJECT")
,
( "name"
, String $
doubleXEncodeGql tableName
<> "_mutation_response"
)
]
)
]
, Object $
HashMap.fromList
[ ("name", String $ "update_" <> doubleXEncodeGql tableName)
,
( "description"
, String $
"Update rows in table \"" <> tableName <> "\""
)
,
( "args"
, List
[ Object $
HashMap.fromList
[ ("name", "filter")
, ("description", "Filter to select rows to be updated")
, ("type", filterType True tableName)
]
, Object $
HashMap.fromList
[ ("name", "set")
, ("description", "Fields to be updated")
,
( "type"
, nonNullType $
Object $
HashMap.fromList
[ ("kind", "INPUT_OBJECT")
,
( "name"
, String $
doubleXEncodeGql tableName
<> "_set_input"
)
]
)
]
]
)
,
( "type"
, nonNullType $
Object $
HashMap.fromList
[ ("kind", "OBJECT")
,
( "name"
, String $
doubleXEncodeGql tableName
<> "_mutation_response"
)
]
)
]
, Object $
HashMap.fromList
[ ("name", String $ "delete_" <> doubleXEncodeGql tableName)
,
( "description"
, String $ "Delete rows in table \"" <> tableName <> "\""
)
,
( "args"
, List
[ Object $
HashMap.fromList
[ ("name", "filter")
, ("description", "Filter to select rows to be deleted")
, ("type", filterType True tableName)
]
]
)
,
( "type"
, nonNullType $
Object $
HashMap.fromList
[ ("kind", "OBJECT")
,
( "name"
, String $
doubleXEncodeGql tableName
<> "_mutation_response"
)
]
)
]
]
makeComparisonType :: Text -> Text -> Value -> Value
makeComparisonType typeName description type_ =
let field fieldName = createField fieldName Nothing type_
in Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "INPUT_OBJECT")
, ("name", String typeName)
,
( "description"
, String description
)
,
( "inputFields"
, List
[ field "eq"
, field "neq"
, field "gt"
, field "gte"
, field "lt"
, field "lte"
, field "like"
, field "ilike"
, createField
"in"
Nothing
(listType type_)
, createField
"nin"
Nothing
(listType type_)
]
)
]
comparisonTypes :: AccessMode -> [Value]
comparisonTypes accessMode =
case accessMode of
ReadOnly -> []
_ ->
[ makeComparisonType "IntComparison" "Compare to an Int" intType
, makeComparisonType "FloatComparison" "Compare to a Float" floatType
, makeComparisonType "StringComparison" "Compare to a String" stringType
, makeComparisonType "BooleanComparison" "Compare to a Boolean" booleanType
]
orderingTermType :: Value
orderingTermType =
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "ENUM")
, ("name", String "OrderingTerm")
,
( "description"
, String "Ordering options when ordering by a column"
)
,
( "enumValues"
, List
[ Object $
HashMap.fromList
[ ("name", "ASC")
, ("description", "In ascending order")
]
, Object $
HashMap.fromList
[ ("name", "asc")
, ("description", "In ascending order")
, ("isDeprecated", Boolean True)
,
( "deprecationReason"
, String "GraphQL spec recommends all caps for enums"
)
]
, Object $
HashMap.fromList
[ ("name", "DESC")
, ("description", "In descending order")
]
, Object $
HashMap.fromList
[ ("name", "desc")
, ("description", "In descending order")
, ("isDeprecated", Boolean True)
,
( "deprecationReason"
, String "GraphQL spec recommends all caps for enums"
)
]
]
)
]
getFullDatatype :: ColumnEntry -> Text
getFullDatatype entry = case entry.datatype_gql of
-- TODO: Should be "Any", but that's not a valid GraphQL type
Nothing -> "String"
Just type_ -> type_.full
getSchemaFieldOutput
:: Text
-> Connection
-> AccessMode
-> [TableEntryRaw]
-> IO (Out.Type IO)
getSchemaFieldOutput dbId conn accessMode tables = do
typesForTables <- forM tables $ \table -> do
columns <- getColumns dbId conn table.name
fields <- forM columns $ \columnEntry -> do
let colName = columnEntry.column_name_gql
pure $
createType
colName
"" -- TODO: Reactivate description when user can specify it
[] -- No arguments
( if columnEntry.notnull
then ["NON_NULL", "SCALAR"]
else ["SCALAR"]
)
(getFullDatatype columnEntry)
fieldsNullable <- forM columns $ \columnEntry -> do
let colName = columnEntry.column_name_gql
pure $
createType
colName
"" -- TODO: Reactivate description when user can specify it
[] -- No arguments
["SCALAR"]
(getFullDatatype columnEntry)
fieldsWithComparisonExp <- forM columns $ \columnEntry -> do
let colName = columnEntry.column_name_gql
pure $
createType
colName
"" -- TODO: Reactivate description when user can specify it
[] -- No arguments
["INPUT_OBJECT"]
(getFullDatatype columnEntry <> "Comparison")
fieldsWithOrderingTerm <- forM columns $ \columnEntry -> do
let colName = columnEntry.column_name_gql
pure $
createType
colName
"" -- TODO: Reactivate description when user can specify it
[] -- No arguments
["INPUT_OBJECT"]
"OrderingTerm"
let
customRowTypes =
columns
>>= \columnEntry ->
case (columnEntry.select_options, columnEntry.datatype_gql) of
(Just _, Just name) ->
let
colName = columnEntry.column_name_gql
typeName = name.full
description = "Data type for column " <> colName
rowType =
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "SCALAR")
, ("name", String typeName)
, ("description", String description)
]
comparisonType =
makeComparisonType
(typeName <> "Comparison")
("Compare with values for column" <> colName)
( Object $
HashMap.fromList
[ ("kind", "SCALAR")
, ("name", String typeName)
, ("description", String description)
]
)
in
[rowType, comparisonType]
_ -> []
fieldEnumVariants =
columns
<&> \columnEntry ->
Object $
HashMap.singleton "name" $
String $
column_name_gql columnEntry
fieldEnumDescription =
"This enum contains a variant for each column in the table" :: Value
fieldEnumType =
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "ENUM")
, ("name", String $ doubleXEncodeGql table.name <> "_column")
,
( "description"
, fieldEnumDescription
)
, ("enumValues", List fieldEnumVariants)
]
fieldEnumTypeReference =
Object $
HashMap.fromList
[ ("kind", "INPUT_OBJECT")
, ("name", String $ doubleXEncodeGql table.name <> "_column")
,
( "description"
, fieldEnumDescription
)
]
requiresWrite obj = case accessMode of
ReadOnly -> Null
WriteOnly -> obj
ReadAndWrite -> obj
pure $
customRowTypes
<> [ Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "OBJECT")
, ("name", String $ doubleXEncodeGql table.name <> "_row")
,
( "description"
, String $
"Available columns for table \""
<> table.name
<> "\""
)
, ("fields", List fields)
]
, requiresWrite $
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "OBJECT")
,
( "name"
, String $
doubleXEncodeGql table.name <> "_mutation_response"
)
,
( "description"
, String $ "Mutation response for " <> table.name
)
,
( "fields"
, List
[ Object $
HashMap.fromList
[ ("name", "affected_rows")
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "SCALAR")
, ("name", "Int")
]
)
]
)
]
, Object $
HashMap.fromList
[ ("name", "returning")
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "LIST")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "OBJECT")
,
( "name"
, String $ doubleXEncodeGql table.name <> "_row"
)
]
)
]
)
]
)
]
)
]
]
)
]
, requiresWrite $
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "INPUT_OBJECT")
,
( "name"
, String $ doubleXEncodeGql table.name <> "_insert_input"
)
,
( "description"
, String $ "Input object for " <> table.name
)
, ("inputFields", List fields)
]
, fieldEnumType
, requiresWrite $
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "INPUT_OBJECT")
,
( "name"
, String $ doubleXEncodeGql table.name <> "_upsert_on_conflict"
)
,
( "description"
, String $ "Specifies how broken UNIQUE constraints for " <> table.name <> " should be handled"
)
,
( "inputFields"
, List
[ createField
"constraint"
(Just "columns to handle conflicts of")
$ nonNullType
$ listType
$ nonNullType fieldEnumTypeReference
, createField
"update_columns"
(Just "columns to override on conflict")
$ nonNullType
$ listType
$ nonNullType fieldEnumTypeReference
, createField
"where"
(Just "filter specifying which conflicting columns to update")
(filterType False table.name)
]
)
]
, requiresWrite $
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "INPUT_OBJECT")
,
( "name"
, String $ doubleXEncodeGql table.name <> "_set_input"
)
,
( "description"
, String $ "Fields to set for " <> table.name
)
, ("inputFields", List fieldsNullable)
]
, requiresWrite $
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "INPUT_OBJECT")
,
( "name"
, String $ doubleXEncodeGql table.name <> "_filter"
)
,
( "description"
, String "Filter object to select rows"
)
, ("inputFields", List fieldsWithComparisonExp)
]
, requiresWrite $
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "INPUT_OBJECT")
,
( "name"
, String $ doubleXEncodeGql table.name <> "_order_by"
)
,
( "description"
, String $
"Ordering options when selecting data from \""
<> table.name
<> "\"."
)
, ("inputFields", List fieldsWithOrderingTerm)
]
]
let
queryTypeObj =
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "OBJECT")
, ("name", "Query")
,
( "fields"
, List $
tables
<&> AirGQL.Lib.name
<&> getFieldsForQuery
)
]
mutationTypeObj =
Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "OBJECT")
, ("name", "Mutation")
,
( "fields"
, List $
tables
<&> AirGQL.Lib.name
<&> getFieldsForMutation
& concat
)
]
pure $
Out.NonNullObjectType $
outObjectTypeToObjectType $
OutObjectType
{ name = "__Schema"
, descriptionMb = Just "__Schema description"
, interfaceTypes = []
, fields =
HashMap.fromList
[
( "__typename"
, ValueResolver nonNullString $ pure "__Schema"
)
,
( "queryType"
, ValueResolver queryTypeType $ pure queryTypeObj
)
,
( "mutationType"
, case accessMode of
ReadOnly -> ValueResolver mutationTypeType $ pure Null
WriteOnly ->
ValueResolver mutationTypeType $ pure mutationTypeObj
ReadAndWrite ->
ValueResolver mutationTypeType $ pure mutationTypeObj
)
,
( "subscriptionType"
, -- AirGQL doesn't support Subscriptions yet
ValueResolver subscriptionTypeType $ pure Null
)
,
( "types"
, ValueResolver typesField $
pure $
List $
concat typesForTables
<> comparisonTypes accessMode
<> [orderingTermType]
<> [ queryTypeObj
, case accessMode of
ReadOnly -> Null
WriteOnly -> mutationTypeObj
ReadAndWrite -> mutationTypeObj
, booleanType
, intType
, floatType
, stringType
, Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "SCALAR")
, ("name", "ID")
,
( "description"
, "The `ID` scalar type represents a unique identifier, \
\often used to refetch an object or as key for a cache. \
\The ID type appears in a JSON response as a String; \
\however, it is not intended to be human-readable. \
\When expected as an input type, any string \
\(such as `\"4\"`) or integer (such as `4`) input value \
\will be accepted as an ID."
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "SCALAR")
, ("name", "Upload")
,
( "description"
, "The `Upload` scalar type represents a file upload."
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "OBJECT")
, ("name", "__Schema")
,
( "description"
, "A GraphQL Schema defines the capabilities of a GraphQL server. \
\It exposes all available types and directives on the server, \
\as well as the entry points for \
\query, mutation, and subscription operations."
)
,
( "fields"
, List
[ Object $
HashMap.fromList
[ ("name", "types")
,
( "description"
, "A list of all types supported by this server."
)
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "LIST")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "OBJECT")
, ("name", "__Type")
]
)
]
)
]
)
]
)
]
, Object $
HashMap.fromList
[ ("name", "queryType")
,
( "description"
, "The type that query operations will be rooted at."
)
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "OBJECT")
, ("name", "__Type")
]
)
]
)
]
, Object $
HashMap.fromList
[ ("name", "mutationType")
,
( "description"
, "If this server supports mutation, the type \
\that mutation operations will be rooted at."
)
, getTypeTuple "OBJECT" "__Type"
]
, Object $
HashMap.fromList
[ ("name", "subscriptionType")
,
( "description"
, "If this server support subscription, the type \
\that subscription operations will be rooted at."
)
, getTypeTuple "OBJECT" "__Type"
]
, Object $
HashMap.fromList
[ ("name", "directives")
,
( "description"
, "A list of all directives supported by this server."
)
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "LIST")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "OBJECT")
, ("name", "__Directive")
]
)
]
)
]
)
]
)
]
]
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "OBJECT")
, ("name", "__Type")
,
( "description"
, "The fundamental unit of any GraphQL Schema is the type. \
\There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\n\
\Depending on the kind of a type, certain fields describe information about that type. \
\Scalar types provide no information beyond a name and description, while Enum types provide their values. \
\Object and Interface types provide the fields they describe. \
\Abstract types, Union and Interface, provide the Object types possible at runtime. \
\List and NonNull types compose other types."
)
,
( "fields"
, List
[ Object $
HashMap.fromList
[ ("name", "kind")
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "ENUM")
, ("name", "__TypeKind")
]
)
]
)
]
, Object $
HashMap.fromList
[ ("name", "name")
, -- Don't know why not "NON_NULL"
getTypeTuple "SCALAR" "String"
]
, descriptionField
, Object $
HashMap.fromList
[ ("name", "fields")
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "LIST")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "OBJECT")
, ("name", "__Field")
]
)
]
)
]
)
,
( "args"
, List
[ Object $
HashMap.fromList
[ ("name", "includeDeprecated")
, getTypeTuple "SCALAR" "Boolean"
, -- Don't know why this has to be a string
("defaultValue", "false")
]
]
)
]
, Object $
HashMap.fromList
[ ("name", "interfaces")
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "LIST")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "OBJECT")
, ("name", "__Type")
]
)
]
)
]
)
]
, Object $
HashMap.fromList
[ ("name", "possibleTypes")
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "LIST")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "OBJECT")
, ("name", "__Type")
]
)
]
)
]
)
]
, Object $
HashMap.fromList
[ ("name", "enumValues")
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "LIST")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "OBJECT")
, ("name", "__EnumValue")
]
)
]
)
]
)
,
( "args"
, List
[ Object $
HashMap.fromList
[ ("name", "includeDeprecated")
, getTypeTuple "SCALAR" "Boolean"
, -- Don't know why this has to be a string
("defaultValue", "false")
]
]
)
]
, Object $
HashMap.fromList
[ ("name", "inputFields")
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "LIST")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "OBJECT")
, ("name", "__InputValue")
]
)
]
)
]
)
]
, Object $
HashMap.fromList
[ ("name", "ofType")
, getTypeTuple "OBJECT" "__Type"
]
]
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "ENUM")
, ("name", "__TypeKind")
,
( "description"
, "An enum describing what kind of type a given `__Type` is."
)
,
( "enumValues"
, List
[ Object $
HashMap.fromList
[ ("name", "SCALAR")
,
( "description"
, "Indicates this type is a scalar."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "OBJECT")
,
( "description"
, "Indicates this type is an object. `fields` and `interfaces` are valid fields."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "INTERFACE")
,
( "description"
, "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "UNION")
,
( "description"
, "Indicates this type is a union. `possibleTypes` is a valid field."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "ENUM")
,
( "description"
, "Indicates this type is an enum. `enumValues` is a valid field."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "INPUT_OBJECT")
,
( "description"
, "Indicates this type is an input object. `inputFields` is a valid field."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "LIST")
,
( "description"
, "Indicates this type is a list. `ofType` is a valid field."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "NON_NULL")
,
( "description"
, "Indicates this type is a non-null. `ofType` is a valid field."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
]
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "OBJECT")
, ("name", "__Field")
,
( "description"
, "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type."
)
,
( "fields"
, List
[ nameField
, descriptionField
, argsFieldValue
, typeFieldValue
, isDeprecatedFieldValue
, Object $
HashMap.fromList
[ ("name", "deprecationReason")
, getTypeTuple "SCALAR" "String"
]
]
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "OBJECT")
, ("name", "__InputValue")
,
( "description"
, "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value."
)
,
( "fields"
, List
[ nameField
, descriptionField
, typeFieldValue
, Object $
HashMap.fromList
[ ("name", "defaultValue")
,
( "description"
, "A GraphQL-formatted string representing \
\the default value for this input value."
)
, getTypeTuple "SCALAR" "String"
]
]
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "OBJECT")
, ("name", "__EnumValue")
,
( "description"
, "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string."
)
,
( "fields"
, List
[ nameField
, descriptionField
, isDeprecatedFieldValue
, Object $
HashMap.fromList
[ ("name", "deprecationReason")
, getTypeTuple "SCALAR" "String"
]
]
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "OBJECT")
, ("name", "__Directive")
,
( "description"
, "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor."
)
,
( "fields"
, List
[ nameField
, descriptionField
, locationsFieldValue
, argsFieldValue
]
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Type")
, ("kind", "ENUM")
, ("name", "__DirectiveLocation")
,
( "description"
, "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies."
)
,
( "enumValues"
, List
[ Object $
HashMap.fromList
[ ("name", "QUERY")
,
( "description"
, "Location adjacent to a query operation."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "MUTATION")
,
( "description"
, "Location adjacent to a mutation operation."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "SUBSCRIPTION")
,
( "description"
, "Location adjacent to a subscription operation."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "FIELD")
,
( "description"
, "Location adjacent to a field."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "FRAGMENT_DEFINITION")
,
( "description"
, "Location adjacent to a fragment definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "FRAGMENT_SPREAD")
,
( "description"
, "Location adjacent to a fragment spread."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "INLINE_FRAGMENT")
,
( "description"
, "Location adjacent to an inline fragment."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "VARIABLE_DEFINITION")
,
( "description"
, "Location adjacent to a variable definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "SCHEMA")
,
( "description"
, "Location adjacent to a schema definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "SCALAR")
,
( "description"
, "Location adjacent to a scalar definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "OBJECT")
,
( "description"
, "Location adjacent to an object type definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "FIELD_DEFINITION")
,
( "description"
, "Location adjacent to a field definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "ARGUMENT_DEFINITION")
,
( "description"
, "Location adjacent to an argument definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "INTERFACE")
,
( "description"
, "Location adjacent to an interface definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "UNION")
,
( "description"
, "Location adjacent to a union definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "ENUM")
,
( "description"
, "Location adjacent to an enum definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "ENUM_VALUE")
,
( "description"
, "Location adjacent to an enum value definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "INPUT_OBJECT")
,
( "description"
, "Location adjacent to an input object \
\type definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
, Object $
HashMap.fromList
[ ("name", "INPUT_FIELD_DEFINITION")
,
( "description"
, "Location adjacent to an input object \
\field definition."
)
, ("isDeprecated", Boolean False)
, ("deprecationReason", Null)
]
]
)
]
]
& filter (/= Null)
)
,
( "directives"
, ValueResolver directivesType $
pure $
List
[ Object $
HashMap.fromList
[ ("__typename", "__Directive")
, ("name", "skip")
,
( "description"
, "Directs the executor to skip this field or fragment \
\when the `if` argument is true."
)
,
( "locations"
, List ["INLINE_FRAGMENT", "FRAGMENT_SPREAD", "FIELD"]
)
,
( "args"
, List
[ Object $
HashMap.fromList
[ ("name", "if")
, ("description", "Skipped when true.")
, ("defaultValue", Null)
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "SCALAR")
, ("name", "Boolean")
]
)
]
)
]
]
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Directive")
, ("name", "include")
,
( "description"
, "Directs the executor to include this field or fragment \
\only when the `if` argument is true."
)
,
( "locations"
, List ["INLINE_FRAGMENT", "FRAGMENT_SPREAD", "FIELD"]
)
,
( "args"
, List
[ Object $
HashMap.fromList
[ ("name", "if")
, ("description", "Included when true.")
, ("defaultValue", Null)
,
( "type"
, Object $
HashMap.fromList
[ ("kind", "NON_NULL")
,
( "ofType"
, Object $
HashMap.fromList
[ ("kind", "SCALAR")
, ("name", "Boolean")
]
)
]
)
]
]
)
]
, Object $
HashMap.fromList
[ ("__typename", "__Directive")
, ("name", "deprecated")
,
( "description"
, "Marks an element of a GraphQL schema \
\as no longer supported."
)
,
( "locations"
, List ["ENUM_VALUE", "FIELD_DEFINITION"]
)
,
( "args"
, List
[ Object $
HashMap.fromList
[ ("name", "reason")
,
( "description"
, "Explains why this element was deprecated, \
\usually also including a suggestion \
\for how to access supported similar data. \
\Formatted using the Markdown syntax \
\(as specified by \
\[CommonMark](https://commonmark.org/)."
)
, ("defaultValue", "\"No longer supported\"")
, getTypeTuple "SCALAR" "String"
]
]
)
]
]
)
]
}
getSchemaField
:: Text
-> Connection
-> AccessMode
-> [TableEntryRaw]
-> IO (Field IO)
getSchemaField dbId conn accessMode tables = do
schemaFieldOutput <- getSchemaFieldOutput dbId conn accessMode tables
pure $
outFieldToField $
OutField
{ descriptionMb = Just "The schema"
, fieldType = schemaFieldOutput
, arguments = HashMap.empty
}
getSchemaResolver
:: Text
-> Connection
-> AccessMode
-> [TableEntryRaw]
-> IO (HashMap Text (Resolver IO))
getSchemaResolver dbId conn accessMode tables = do
schemaField <- getSchemaField dbId conn accessMode tables
pure $
HashMap.singleton
"__schema"
(ValueResolver schemaField (pure Null))
typeNameOutField :: Field m
typeNameOutField =
outFieldToField $
OutField
{ descriptionMb = Just "The type name"
, fieldType = Out.NonNullScalarType string
, arguments = HashMap.empty
}
typeNameResolver :: HashMap Text (Resolver IO)
typeNameResolver =
HashMap.singleton
"__typename"
(ValueResolver typeNameOutField $ pure "Query")