Support default values (in inputvalue) from the remote schema (close #1491) (#1493)

This commit is contained in:
nizar-m 2019-01-28 18:08:38 +05:30 committed by Vamshi Surabhi
parent fc73d4d30a
commit 8e3b8f51c9
10 changed files with 248 additions and 67 deletions

View File

@ -188,7 +188,7 @@ mkCompExpInp colTy =
, bool [] (map (mk $ mkScalarTy PGText) stringOps) isStringTy
, bool [] (map jsonbOpToInpVal jsonbOps) isJsonbTy
, bool [] (stDWithinOpInpVal : map geomOpToInpVal geomOps) isGeometryTy
, [InpValInfo Nothing "_is_null" $ G.TypeNamed (G.Nullability True) $ G.NamedType "Boolean"]
, [InpValInfo Nothing "_is_null" Nothing $ G.TypeNamed (G.Nullability True) $ G.NamedType "Boolean"]
]) HasuraType
where
tyDesc = mconcat
@ -200,7 +200,7 @@ mkCompExpInp colTy =
PGVarchar -> True
PGText -> True
_ -> False
mk t n = InpValInfo Nothing n $ G.toGT t
mk t n = InpValInfo Nothing n Nothing $ G.toGT t
colScalarTy = mkScalarTy colTy
-- colScalarListTy = GA.GTList colGTy
typedOps =
@ -218,7 +218,7 @@ mkCompExpInp colTy =
isJsonbTy = case colTy of
PGJSONB -> True
_ -> False
jsonbOpToInpVal (op, ty, desc) = InpValInfo (Just desc) op ty
jsonbOpToInpVal (op, ty, desc) = InpValInfo (Just desc) op Nothing ty
jsonbOps =
[ ( "_contains"
, G.toGT $ mkScalarTy PGJSONB
@ -244,7 +244,7 @@ mkCompExpInp colTy =
-- Geometry related ops
stDWithinOpInpVal =
InpValInfo (Just stDWithinDesc) "_st_d_within" $ G.toGT stDWithinInpTy
InpValInfo (Just stDWithinDesc) "_st_d_within" Nothing $ G.toGT stDWithinInpTy
stDWithinDesc =
"is the column within a distance from a geometry value"
@ -253,7 +253,7 @@ mkCompExpInp colTy =
_ -> False
geomOpToInpVal (op, desc) =
InpValInfo (Just desc) op $ G.toGT $ mkScalarTy PGGeometry
InpValInfo (Just desc) op Nothing $ G.toGT $ mkScalarTy PGGeometry
geomOps =
[
( "_st_contains"
@ -352,15 +352,15 @@ mkGCtx (TyAgg tyInfos fldInfos ordByEnums funcArgCtx) (RootFlds flds) insCtxMap
G.toGT $ G.NamedType "__Type"
where
typeFldArgs = mapFromL _iviName [
InpValInfo (Just "name of the type") "name"
InpValInfo (Just "name of the type") "name" Nothing
$ G.toGT $ G.toNT $ G.NamedType "String"
]
stDWithinInpM = bool Nothing (Just stDWithinInp) (PGGeometry `elem` colTys)
stDWithinInp =
mkHsraInpTyInfo Nothing stDWithinInpTy $ fromInpValL
[ InpValInfo Nothing "from" $ G.toGT $ G.toNT $ mkScalarTy PGGeometry
, InpValInfo Nothing "distance" $ G.toNT $ G.toNT $ mkScalarTy PGFloat
[ InpValInfo Nothing "from" Nothing $ G.toGT $ G.toNT $ mkScalarTy PGGeometry
, InpValInfo Nothing "distance" Nothing $ G.toNT $ G.toNT $ mkScalarTy PGFloat
]
emptyGCtx :: GCtx

View File

@ -14,6 +14,7 @@ import qualified Data.HashMap.Strict as Map
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Language.GraphQL.Draft.Syntax as G
import qualified Language.GraphQL.Draft.Parser as G
import qualified Network.HTTP.Client as HTTP
import qualified Network.Wreq as Wreq
@ -25,6 +26,7 @@ import qualified Hasura.GraphQL.Schema as GS
import qualified Hasura.GraphQL.Validate.Types as VT
introspectionQuery :: BL.ByteString
introspectionQuery = $(embedStringFile "src-rsr/introspection.json")
@ -265,11 +267,15 @@ instance J.FromJSON (FromIntrospection G.InputValueDefinition) where
name <- o .: "name"
desc <- o .:? "description"
_type <- o .: "type"
--defValue <- o .: "defaultValue"
defVal <- o .:? "defaultValue"
let desc' = fmap fromIntrospection desc
r = G.InputValueDefinition desc' name (fromIntrospection _type) Nothing
let defVal' = fmap fromIntrospection defVal
r = G.InputValueDefinition desc' name (fromIntrospection _type) defVal'
return $ FromIntrospection r
instance J.FromJSON (FromIntrospection G.ValueConst) where
parseJSON = J.withText "defaultValue" $ \t -> fmap FromIntrospection
$ either (fail . T.unpack) return $ G.parseValueConst t
-- instance J.FromJSON (FromIntrospection G.ListType) where
-- parseJSON = parseJSON
@ -284,11 +290,6 @@ instance J.FromJSON (FromIntrospection G.InputValueDefinition) where
-- ofVal <- o .: "value"
-- return $ FromIntrospection $ G.ObjectFieldG name ofVal
-- instance J.FromJSON (FromIntrospection G.ValueConst) where
-- parseJSON =
-- fmap FromIntrospection .
-- $(J.mkParseJSON J.defaultOptions{J.sumEncoding=J.UntaggedValue} ''G.ValueConst)
-- instance J.FromJSON (FromIntrospection G.Value) where
-- parseJSON =
-- fmap FromIntrospection .

View File

@ -13,6 +13,7 @@ import qualified Language.GraphQL.Draft.Syntax as G
import Hasura.GraphQL.Resolve.Context
import Hasura.GraphQL.Resolve.InputValue
import Hasura.GraphQL.Validate.InputValue
import Hasura.GraphQL.Validate.Context
import Hasura.GraphQL.Validate.Field
import Hasura.GraphQL.Validate.Types
@ -201,7 +202,7 @@ inputValueR
:: ( MonadReader r m, Has TypeMap r
, MonadError QErr m)
=> Field -> InpValInfo -> m J.Object
inputValueR fld (InpValInfo descM n ty) =
inputValueR fld (InpValInfo descM n defM ty) =
withSubFields (_fSelSet fld) $ \subFld ->
case _fName subFld of
"__typename" -> retJT "__InputValue"
@ -209,7 +210,7 @@ inputValueR fld (InpValInfo descM n ty) =
"description" -> retJ $ fmap G.unDescription descM
"type" -> J.toJSON <$> gtypeR ty subFld
-- TODO: figure out what the spec means by 'string encoding'
"defaultValue" -> return J.Null
"defaultValue" -> retJ $ pPrintValueC <$> defM
_ -> return J.Null
-- 4.5.5

View File

@ -348,12 +348,12 @@ mkPGColFld (PGColInfo colName colTy isNullable) =
-- distinct_on: [table_select_column!]
mkSelArgs :: QualifiedTable -> [InpValInfo]
mkSelArgs tn =
[ InpValInfo (Just whereDesc) "where" $ G.toGT $ mkBoolExpTy tn
, InpValInfo (Just limitDesc) "limit" $ G.toGT $ mkScalarTy PGInteger
, InpValInfo (Just offsetDesc) "offset" $ G.toGT $ mkScalarTy PGInteger
, InpValInfo (Just orderByDesc) "order_by" $ G.toGT $ G.toLT $ G.toNT $
[ InpValInfo (Just whereDesc) "where" Nothing $ G.toGT $ mkBoolExpTy tn
, InpValInfo (Just limitDesc) "limit" Nothing $ G.toGT $ mkScalarTy PGInteger
, InpValInfo (Just offsetDesc) "offset" Nothing $ G.toGT $ mkScalarTy PGInteger
, InpValInfo (Just orderByDesc) "order_by" Nothing $ G.toGT $ G.toLT $ G.toNT $
mkOrdByTy tn
, InpValInfo (Just distinctDesc) "distinct_on" $ G.toGT $ G.toLT $
, InpValInfo (Just distinctDesc) "distinct_on" Nothing $ G.toGT $ G.toLT $
G.toNT $ mkSelColumnInpTy tn
]
where
@ -471,9 +471,9 @@ mkTableAggFldsObj tn numCols compCols =
countParams = fromInpValL [countColInpVal, distinctInpVal]
countColInpVal = InpValInfo Nothing "columns" $ G.toGT $
countColInpVal = InpValInfo Nothing "columns" Nothing $ G.toGT $
G.toLT $ G.toNT $ mkSelColumnInpTy tn
distinctInpVal = InpValInfo Nothing "distinct" $ G.toGT $
distinctInpVal = InpValInfo Nothing "distinct" Nothing $ G.toGT $
mkScalarTy PGBoolean
numFlds = bool (map mkColOpFld numAggOps) [] $ null numCols
@ -544,7 +544,7 @@ mkSelFldPKey tn cols =
args = fromInpValL $ map colInpVal cols
ty = G.toGT $ mkTableTy tn
colInpVal (PGColInfo n typ _) =
InpValInfo Nothing (mkColName n) $ G.toGT $ G.toNT $ mkScalarTy typ
InpValInfo Nothing (mkColName n) Nothing $ G.toGT $ G.toNT $ mkScalarTy typ
{-
@ -587,7 +587,7 @@ mkFuncArgs funInfo =
retTable = fiReturnType funInfo
funcArgDesc = G.Description $ "input parameters for function " <>> funcName
funcInpArg = InpValInfo (Just funcArgDesc) "args" $ G.toGT $ G.toNT $
funcInpArg = InpValInfo (Just funcArgDesc) "args" Nothing $ G.toGT $ G.toNT $
mkFuncArgsTy funcName
funcInpArgs = bool [funcInpArg] [] $ null funcArgs
@ -685,7 +685,7 @@ mkBoolExpInp tn fields =
-- all the fields of this input object
inpValues = combinators <> map mkFldExpInp fields
mk n ty = InpValInfo Nothing n $ G.toGT ty
mk n ty = InpValInfo Nothing n Nothing $ G.toGT ty
boolExpListTy = G.toLT boolExpTy
@ -703,7 +703,7 @@ mkBoolExpInp tn fields =
mkPGColInp :: PGColInfo -> InpValInfo
mkPGColInp (PGColInfo colName colTy _) =
InpValInfo Nothing (G.Name $ getPGColTxt colName) $
InpValInfo Nothing (G.Name $ getPGColTxt colName) Nothing $
G.toGT $ mkScalarTy colTy
{-
@ -732,13 +732,13 @@ mkFuncArgsInp funcInfo =
case nameM of
Just argName ->
let argGName = G.Name $ getFuncArgNameTxt argName
inpVal = InpValInfo Nothing argGName $
inpVal = InpValInfo Nothing argGName Nothing $
G.toGT $ G.toNT $ mkScalarTy ty
argCtxItem = FuncArgItem argGName
in (items <> pure (inpVal, argCtxItem), argNo)
Nothing ->
let argGName = G.Name $ "arg_" <> T.pack (show argNo)
inpVal = InpValInfo Nothing argGName $
inpVal = InpValInfo Nothing argGName Nothing $
G.toGT $ G.toNT $ mkScalarTy ty
argCtxItem = FuncArgItem argGName
in (items <> pure (inpVal, argCtxItem), argNo + 1)
@ -889,19 +889,19 @@ mkUpdJSONOpInp tn cols = bool inpObjs [] $ null jsonbCols
deleteKeyInpObj =
mkHsraInpTyInfo (Just deleteKeyDesc) (mkJSONOpTy tn deleteKeyOp) $
fromInpValL $ map deleteKeyInpVal jsonbColNames
deleteKeyInpVal c = InpValInfo Nothing (G.Name $ getPGColTxt c) $
deleteKeyInpVal c = InpValInfo Nothing (G.Name $ getPGColTxt c) Nothing $
G.toGT $ G.NamedType "String"
deleteElemInpObj =
mkHsraInpTyInfo (Just deleteElemDesc) (mkJSONOpTy tn deleteElemOp) $
fromInpValL $ map deleteElemInpVal jsonbColNames
deleteElemInpVal c = InpValInfo Nothing (G.Name $ getPGColTxt c) $
deleteElemInpVal c = InpValInfo Nothing (G.Name $ getPGColTxt c) Nothing $
G.toGT $ G.NamedType "Int"
deleteAtPathInpObj =
mkHsraInpTyInfo (Just deleteAtPathDesc) (mkJSONOpTy tn deleteAtPathOp) $
fromInpValL $ map deleteAtPathInpVal jsonbColNames
deleteAtPathInpVal c = InpValInfo Nothing (G.Name $ getPGColTxt c) $
deleteAtPathInpVal c = InpValInfo Nothing (G.Name $ getPGColTxt c) Nothing $
G.toGT $ G.toLT $ G.NamedType "String"
{-
@ -924,7 +924,7 @@ mkIncInpVal tn cols = bool (Just incArg) Nothing $ null intCols
intCols = onlyIntCols cols
incArgDesc = "increments the integer columns with given value of the filtered values"
incArg =
InpValInfo (Just incArgDesc) "_inc" $ G.toGT $ mkUpdIncTy tn
InpValInfo (Just incArgDesc) "_inc" Nothing $ G.toGT $ mkUpdIncTy tn
mkJSONOpInpVals :: QualifiedTable -> [PGColInfo] -> [InpValInfo]
mkJSONOpInpVals tn cols = bool jsonbOpArgs [] $ null jsonbCols
@ -933,21 +933,21 @@ mkJSONOpInpVals tn cols = bool jsonbOpArgs [] $ null jsonbCols
jsonbOpArgs = [appendArg, prependArg, deleteKeyArg, deleteElemArg, deleteAtPathArg]
appendArg =
InpValInfo (Just appendDesc) appendOp $ G.toGT $ mkJSONOpTy tn appendOp
InpValInfo (Just appendDesc) appendOp Nothing $ G.toGT $ mkJSONOpTy tn appendOp
prependArg =
InpValInfo (Just prependDesc) prependOp $ G.toGT $ mkJSONOpTy tn prependOp
InpValInfo (Just prependDesc) prependOp Nothing $ G.toGT $ mkJSONOpTy tn prependOp
deleteKeyArg =
InpValInfo (Just deleteKeyDesc) deleteKeyOp $
InpValInfo (Just deleteKeyDesc) deleteKeyOp Nothing $
G.toGT $ mkJSONOpTy tn deleteKeyOp
deleteElemArg =
InpValInfo (Just deleteElemDesc) deleteElemOp $
InpValInfo (Just deleteElemDesc) deleteElemOp Nothing $
G.toGT $ mkJSONOpTy tn deleteElemOp
deleteAtPathArg =
InpValInfo (Just deleteAtPathDesc) deleteAtPathOp $
InpValInfo (Just deleteAtPathDesc) deleteAtPathOp Nothing $
G.toGT $ mkJSONOpTy tn deleteAtPathOp
mkUpdMutFld
@ -964,12 +964,12 @@ mkUpdMutFld tn cols =
filterArgDesc = "filter the rows which have to be updated"
filterArg =
InpValInfo (Just filterArgDesc) "where" $ G.toGT $
InpValInfo (Just filterArgDesc) "where" Nothing $ G.toGT $
G.toNT $ mkBoolExpTy tn
setArgDesc = "sets the columns of the filtered rows to the given values"
setArg =
InpValInfo (Just setArgDesc) "_set" $ G.toGT $ mkUpdSetTy tn
InpValInfo (Just setArgDesc) "_set" Nothing $ G.toGT $ mkUpdSetTy tn
incArg = maybeToList $ mkIncInpVal tn cols
@ -993,7 +993,7 @@ mkDelMutFld tn =
filterArgDesc = "filter the rows which have to be deleted"
filterArg =
InpValInfo (Just filterArgDesc) "where" $ G.toGT $
InpValInfo (Just filterArgDesc) "where" Nothing $ G.toGT $
G.toNT $ mkBoolExpTy tn
-- table_insert_input
@ -1057,14 +1057,14 @@ mkRelInsInps
mkRelInsInps tn upsertAllowed = [objRelInsInp, arrRelInsInp]
where
onConflictInpVal =
InpValInfo Nothing "on_conflict" $ G.toGT $ mkOnConflictInpTy tn
InpValInfo Nothing "on_conflict" Nothing $ G.toGT $ mkOnConflictInpTy tn
onConflictInp = bool [] [onConflictInpVal] upsertAllowed
objRelDesc = G.Description $
"input type for inserting object relation for remote table " <>> tn
objRelDataInp = InpValInfo Nothing "data" $ G.toGT $
objRelDataInp = InpValInfo Nothing "data" Nothing $ G.toGT $
G.toNT $ mkInsInpTy tn
objRelInsInp = mkHsraInpTyInfo (Just objRelDesc) (mkObjInsInpTy tn)
$ fromInpValL $ objRelDataInp : onConflictInp
@ -1072,7 +1072,7 @@ mkRelInsInps tn upsertAllowed = [objRelInsInp, arrRelInsInp]
arrRelDesc = G.Description $
"input type for inserting array relation for remote table " <>> tn
arrRelDataInp = InpValInfo Nothing "data" $ G.toGT $
arrRelDataInp = InpValInfo Nothing "data" Nothing $ G.toGT $
G.toNT $ G.toLT $ G.toNT $ mkInsInpTy tn
arrRelInsInp = mkHsraInpTyInfo (Just arrRelDesc) (mkArrInsInpTy tn)
$ fromInpValL $ arrRelDataInp : onConflictInp
@ -1106,9 +1106,9 @@ mkInsInp tn insCtx =
let rty = riType relInfo
remoteQT = riRTable relInfo
in case rty of
ObjRel -> InpValInfo Nothing (G.Name $ getRelTxt relName) $
ObjRel -> InpValInfo Nothing (G.Name $ getRelTxt relName) Nothing $
G.toGT $ mkObjInsInpTy remoteQT
ArrRel -> InpValInfo Nothing (G.Name $ getRelTxt relName) $
ArrRel -> InpValInfo Nothing (G.Name $ getRelTxt relName) Nothing $
G.toGT $ mkArrInsInpTy remoteQT
{-
@ -1129,10 +1129,10 @@ mkOnConflictInp tn =
desc = G.Description $
"on conflict condition type for table " <>> tn
constraintInpVal = InpValInfo Nothing (G.Name "constraint") $
constraintInpVal = InpValInfo Nothing (G.Name "constraint") Nothing $
G.toGT $ G.toNT $ mkConstraintInpTy tn
updateColumnsInpVal = InpValInfo Nothing (G.Name "update_columns") $
updateColumnsInpVal = InpValInfo Nothing (G.Name "update_columns") Nothing $
G.toGT $ G.toNT $ G.toLT $ G.toNT $ mkUpdColumnInpTy tn
{-
@ -1156,14 +1156,14 @@ mkInsMutFld tn isUpsertable =
objsArgDesc = "the rows to be inserted"
objectsArg =
InpValInfo (Just objsArgDesc) "objects" $ G.toGT $
InpValInfo (Just objsArgDesc) "objects" Nothing $ G.toGT $
G.toNT $ G.toLT $ G.toNT $ mkInsInpTy tn
onConflictInpVal = bool Nothing (Just onConflictArg) isUpsertable
onConflictDesc = "on conflict condition"
onConflictArg =
InpValInfo (Just onConflictDesc) "on_conflict" $ G.toGT $ mkOnConflictInpTy tn
InpValInfo (Just onConflictDesc) "on_conflict" Nothing $ G.toGT $ mkOnConflictInpTy tn
mkConstriantTy :: QualifiedTable -> [ConstraintName] -> EnumTyInfo
mkConstriantTy tn cons = enumTyInfo
@ -1262,7 +1262,7 @@ mkTabAggOpOrdByInpObjs tn numCols compCols =
mkInpObjTy cols op = mkHsraInpTyInfo (Just $ mkDesc op) (mkTabAggOpOrdByTy tn op) $
fromInpValL $ map mkColInpVal cols
mkColInpVal c = InpValInfo Nothing (mkColName c) $ G.toGT
mkColInpVal c = InpValInfo Nothing (mkColName c) Nothing $ G.toGT
ordByTy
mkTabAggOrdByTy :: QualifiedTable -> G.NamedType
@ -1287,10 +1287,10 @@ mkTabAggOrdByInpObj tn numCols compCols =
numOpOrdBys = bool (map mkInpValInfo numAggOps) [] $ null numCols
compOpOrdBys = bool (map mkInpValInfo compAggOps) [] $ null compCols
mkInpValInfo op = InpValInfo Nothing op $ G.toGT $
mkInpValInfo op = InpValInfo Nothing op Nothing $ G.toGT $
mkTabAggOpOrdByTy tn op
countInpVal = InpValInfo Nothing "count" $ G.toGT ordByTy
countInpVal = InpValInfo Nothing "count" Nothing $ G.toGT ordByTy
mkOrdByTy :: QualifiedTable -> G.NamedType
mkOrdByTy tn =
@ -1326,14 +1326,14 @@ mkOrdByInpObj tn selFlds = (inpObjTy, ordByCtx)
objRels = relFltr ObjRel
arrRels = relFltr ArrRel
mkColOrdBy ci = InpValInfo Nothing (mkColName $ pgiName ci) $
mkColOrdBy ci = InpValInfo Nothing (mkColName $ pgiName ci) Nothing $
G.toGT ordByTy
mkObjRelOrdBy (ri, _, _, _, _) =
InpValInfo Nothing (mkRelName $ riName ri) $
InpValInfo Nothing (mkRelName $ riName ri) Nothing $
G.toGT $ mkOrdByTy $ riRTable ri
mkArrRelAggOrdBy (ri, isAggAllowed, _, _, _) =
let ivi = InpValInfo Nothing (mkAggRelName $ riName ri) $
let ivi = InpValInfo Nothing (mkAggRelName $ riName ri) Nothing $
G.toGT $ mkTabAggOrdByTy $ riRTable ri
in bool Nothing (Just ivi) isAggAllowed

View File

@ -3,6 +3,7 @@ module Hasura.GraphQL.Validate.InputValue
, jsonParser
, valueParser
, constValueParser
, pPrintValueC
) where
import Data.Scientific (fromFloatDigits)
@ -14,6 +15,7 @@ import Data.Has
import qualified Data.Aeson as J
import qualified Data.HashMap.Strict as Map
import qualified Data.HashMap.Strict.InsOrd as OMap
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Language.GraphQL.Draft.Syntax as G
@ -134,6 +136,22 @@ valueParser =
pScalar (G.VEnum _) = throwVE "unexpected enum for a scalar"
pScalar v = pVal =<< toJValue v
pPrintValueC :: G.ValueConst -> Text
pPrintValueC = \case
G.VCInt i -> T.pack $ show i
G.VCFloat f -> T.pack $ show f
G.VCString (G.StringValue t) -> T.pack $ show t
G.VCBoolean b -> bool "false" "true" b
G.VCNull -> "null"
G.VCEnum (G.EnumValue n) -> G.unName n
G.VCList (G.ListValueG vals) -> withSquareBraces $ T.intercalate ", " $ map pPrintValueC vals
G.VCObject (G.ObjectValueG objs) -> withCurlyBraces $ T.intercalate ", " $ map ppObjFld objs
where
ppObjFld (G.ObjectFieldG f v) = G.unName f <> ": " <> pPrintValueC v
withSquareBraces t = "[" <> t <> "]"
withCurlyBraces t = "{" <> t <> "}"
toJValueC :: G.ValueConst -> J.Value
toJValueC = \case
G.VCInt i -> J.toJSON i
@ -145,7 +163,7 @@ toJValueC = \case
G.VCList (G.ListValueG vals) ->
J.toJSON $ map toJValueC vals
G.VCObject (G.ObjectValueG objs) ->
J.toJSON . Map.fromList $ map toTup objs
J.toJSON . OMap.fromList $ map toTup objs
where
toTup (G.ObjectFieldG f v) = (f, toJValueC v)

View File

@ -98,10 +98,10 @@ fromEnumTyDef (G.EnumTypeDefinition descM n _ valDefs) loc =
data InpValInfo
= InpValInfo
{ _iviDesc :: !(Maybe G.Description)
, _iviName :: !G.Name
, _iviType :: !G.GType
-- TODO, handle default values
{ _iviDesc :: !(Maybe G.Description)
, _iviName :: !G.Name
, _iviDefVal :: !(Maybe G.ValueConst)
, _iviType :: !G.GType
} deriving (Show, Eq, TH.Lift)
instance EquatableGType InpValInfo where
@ -109,8 +109,8 @@ instance EquatableGType InpValInfo where
getEqProps ity = (,) (_iviName ity) (_iviType ity)
fromInpValDef :: G.InputValueDefinition -> InpValInfo
fromInpValDef (G.InputValueDefinition descM n ty _) =
InpValInfo descM n ty
fromInpValDef (G.InputValueDefinition descM n ty defM) =
InpValInfo descM n defM ty
type ParamMap = Map.HashMap G.Name InpValInfo
@ -317,8 +317,8 @@ defaultDirectives =
[mkDirective "skip", mkDirective "include"]
where
mkDirective n = DirectiveInfo Nothing n args dirLocs
args = Map.singleton "if" $ InpValInfo Nothing "if" $
G.TypeNamed (G.Nullability True) $ G.NamedType $ G.Name "Boolean"
args = Map.singleton "if" $ InpValInfo Nothing "if" Nothing $
G.TypeNamed (G.Nullability False) $ G.NamedType $ G.Name "Boolean"
dirLocs = map G.DLExecutable
[G.EDLFIELD, G.EDLFRAGMENT_SPREAD, G.EDLINLINE_FRAGMENT]

View File

@ -19,7 +19,7 @@ extra-deps:
- git: https://github.com/hasura/pg-client-hs.git
commit: f3d1e9e67bdfbfa3de85b7cbdb4c557dce7fd84d
- git: https://github.com/hasura/graphql-parser-hs.git
commit: 75782ae894cce05ed31e5b87fd696fc10e88baf9
commit: ff95d9a96aa5ef9e5390f8712958e4118e3831f6
- ginger-0.8.1.0
# for text-builder

View File

@ -6,6 +6,8 @@ import graphene
from webserver import RequestHandler, WebServer, MkHandlers, Response
from enum import Enum
def mkJSONResp(graphql_result):
return Response(HTTPStatus.OK, graphql_result.to_dict(),
{'Content-Type': 'application/json'})
@ -151,11 +153,82 @@ class PersonGraphQL(RequestHandler):
res = person_schema.execute(req.json['query'])
return mkJSONResp(res)
class InpObjType(graphene.InputObjectType):
@classmethod
def default(cls):
meta = cls._meta
fields = meta.fields
default_fields = {name: field.default_value for name, field in fields.items()}
container = meta.container
return container(**default_fields)
class SizeObj(graphene.ObjectType):
width = graphene.Int()
height = graphene.Float()
shape = graphene.String()
hasTag = graphene.Boolean()
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
GQColorEnum = graphene.Enum.from_enum(Color)
class SizeInput(InpObjType):
width = graphene.Int(default_value=100)
height = graphene.Float(default_value=100.1)
shape = graphene.String(default_value="cube")
hasTag = graphene.Boolean(default_value=False)
def asSizeObj(self):
return SizeObj(width=self.width, height=self.height, shape=self.shape, hasTag=self.hasTag)
class Echo(graphene.ObjectType):
intFld = graphene.Int()
listFld = graphene.List(graphene.String)
objFld = graphene.Field(SizeObj)
enumFld = graphene.Field(GQColorEnum)
class EchoQuery(graphene.ObjectType):
echo = graphene.Field(
Echo,
int_input=graphene.Int( default_value=1234),
list_input=graphene.Argument(graphene.List(graphene.String), default_value=["hi","there"]),
obj_input=graphene.Argument(SizeInput, default_value=SizeInput.default()),
enum_input=graphene.Argument(GQColorEnum, default_value=GQColorEnum.RED.name),
)
def resolve_echo(self, info, int_input, list_input, obj_input, enum_input):
#print (int_input, list_input, obj_input)
return Echo(intFld=int_input, listFld=list_input, objFld=obj_input, enumFld=enum_input)
echo_schema = graphene.Schema(query=EchoQuery)
class EchoGraphQL(RequestHandler):
def get(self, req):
return Response(HTTPStatus.METHOD_NOT_ALLOWED)
def post(self, req):
if not req.json:
return Response(HTTPStatus.BAD_REQUEST)
res = echo_schema.execute(req.json['query'])
respDict = res.to_dict()
typesList = respDict.get('data',{}).get('__schema',{}).get('types',None)
if typesList is not None:
for t in filter(lambda ty: ty['name'] == 'EchoQuery', typesList):
for f in filter(lambda fld: fld['name'] == 'echo', t['fields']):
for a in filter(lambda arg: arg['name'] == 'enumInput', f['args']):
a['defaultValue'] = 'RED'
return Response(HTTPStatus.OK, respDict,
{'Content-Type': 'application/json'})
handlers = MkHandlers({
'/hello': HelloWorldHandler,
'/hello-graphql': HelloGraphQL,
'/user-graphql': UserGraphQL,
'/country-graphql': CountryGraphQL,
'/default-value-echo-graphql' : EchoGraphQL,
'/person-graphql': PersonGraphQL
})

View File

@ -2,6 +2,7 @@
import pytest
import yaml
import requests
from validate import check_query_f, check_query
@ -19,6 +20,14 @@ def mk_add_remote_q(name, url):
}
}
def mk_delete_remote_q(name):
return {
"type" : "remove_remote_schema",
"args" : {
"name": name
}
}
class TestRemoteSchemaBasic:
""" basic => no hasura tables are tracked """
@ -156,6 +165,62 @@ class TestAddRemoteSchemaTbls:
hge_ctx.v1q({"type": "remove_remote_schema", "args": {"name": "person-graphql"}})
assert st_code == 200, resp
class TestAddRemoteSchemaCompareRootQueryFields:
remote = 'http://localhost:5000/default-value-echo-graphql'
@pytest.fixture(autouse=True)
def transact(self, hge_ctx):
st_code, resp = hge_ctx.v1q(mk_add_remote_q('default_value_test', self.remote))
assert st_code == 200, resp
yield
st_code, resp = hge_ctx.v1q(mk_delete_remote_q('default_value_test'))
assert st_code == 200, resp
def test_schema_check_arg_default_values_and_field_and_arg_types(self, hge_ctx):
with open('queries/graphql_introspection/introspection.yaml') as f:
query = yaml.load(f)
st_code, introspect_hasura = check_query(hge_ctx, query)
assert st_code == 200, introspect_hasura
resp = requests.post(
self.remote,
json=query['query']
)
introspect_remote = resp.json()
assert resp.status_code == 200, introspect_remote
remote_root_ty_info = get_query_root_info(introspect_remote)
hasura_root_ty_Info = get_query_root_info(introspect_hasura)
hasFld=dict()
for fr in remote_root_ty_info['fields']:
hasFld[fr['name']] = False
for fh in filter(lambda f: f['name'] == fr['name'], hasura_root_ty_Info['fields']):
hasFld[fr['name']] = True
assert fr['type'] == fh['type'], yaml.dump({
'error' : 'Types do not match for fld ' + fr['name'],
'remote_type' : fr['type'],
'hasura_type' : fh['type']
})
hasArg=dict()
for ar in fr['args']:
arPath = fr['name'] + '(' + ar['name'] + ':)'
hasArg[arPath] = False
for ah in filter(lambda a: a['name'] == ar['name'], fh['args']):
hasArg[arPath] = True
assert ar['type'] == ah['type'], yaml.dump({
'error' : 'Types do not match for arg ' + arPath,
'remote_type' : ar['type'],
'hasura_type' : ah['type']
})
assert ar['defaultValue'] == ah['defaultValue'], yaml.dump({
'error' : 'Default values do not match for arg ' + arPath,
'remote_default_value' : ar['defaultValue'],
'hasura_default_value' : ah['defaultValue']
})
assert hasArg[arPath], 'Argument ' + arPath + ' in the remote schema root query type not found in Hasura schema'
assert hasFld[fr['name']], 'Field ' + fr['name'] + ' in the remote shema root query type not found in Hasura schema'
# def test_remote_query_variables(self, hge_ctx):
# pass
# def test_add_schema_url_from_env(self, hge_ctx):
@ -170,6 +235,13 @@ def _map(f, l):
def _filter(f, l):
return list(filter(f, l))
def get_query_root_info(res):
root_ty_name = res['data']['__schema']['queryType']['name']
return list(filter(lambda ty: ty['name'] == root_ty_name, get_types(res) ))[0]
def get_types(res):
return res['data']['__schema']['types']
def check_introspection_result(res, types, node_names):
all_types = _map(lambda t: t['name'], res['data']['__schema']['types'])
print(all_types)

View File

@ -101,11 +101,27 @@ def MkHandlers(handlers):
self.send_response(resp.status)
if resp.headers:
self.append_headers(resp.headers)
#Required for graphiql to work with the graphQL test server
self.send_header('Access-Control-Allow-Origin', self.headers['Origin'])
self.send_header('Access-Control-Allow-Credentials', 'true')
self.send_header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS')
self.end_headers()
self.wfile.write(resp.get_body().encode('utf-8'))
except KeyError:
self.not_found()
def do_OPTIONS(self):
self.send_response(204)
#Required for graphiql to work with the graphQL test server
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Max-Age', '1728000')
self.send_header('Access-Control-Allow-Headers', 'content-type,x-apollo-tracing')
self.send_header('Content-Type', 'text/plain charset=UTF-8')
self.send_header('Access-Control-Allow-Credentials', 'true')
self.send_header('Access-Control-Allow-Origin', self.headers['Origin'])
self.send_header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS')
self.end_headers()
return HTTPHandler