Skip to content

Commit

Permalink
auto-include __typename field in custom types' objects (fix #4063) (#…
Browse files Browse the repository at this point in the history
…4074)

* include `__typename` field in custom types' objects, fix #4063

Co-authored-by: Vamshi Surabhi <0x777@users.noreply.github.com>
  • Loading branch information
rakeshkky and 0x777 authored Mar 11, 2020
1 parent f97937c commit 996ce92
Show file tree
Hide file tree
Showing 10 changed files with 40 additions and 46 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,4 @@
- add meta descriptions to actions docs (#4082)
- `HASURA_GRAPHQL_EVENTS_FETCH_INTERVAL` changes semantics slightly: we only sleep for the interval
when there were previously no events to process. Potential space leak fixed. (#3839)
- auto-include `__typename` field in custom types' objects (fix #4063)
38 changes: 2 additions & 36 deletions server/src-lib/Hasura/GraphQL/RemoteServer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import Hasura.Prelude
import qualified Data.Aeson as J
import qualified Data.ByteString.Lazy as BL
import qualified Data.HashMap.Strict as Map
import qualified Data.HashSet as Set
import qualified Data.Text as T
import qualified Language.GraphQL.Draft.Parser as G
import qualified Language.GraphQL.Draft.Syntax as G
Expand Down Expand Up @@ -159,43 +158,10 @@ mergeQueryRoot :: GS.GCtx -> GS.GCtx -> VT.ObjTyInfo
mergeQueryRoot a b = GS._gQueryRoot a <> GS._gQueryRoot b

mergeMutRoot :: GS.GCtx -> GS.GCtx -> Maybe VT.ObjTyInfo
mergeMutRoot a b =
let objA' = fromMaybe mempty $ GS._gMutRoot a
objB = fromMaybe mempty $ GS._gMutRoot b
objA = newRootOrEmpty objA' objB
merged = objA <> objB
in bool (Just merged) Nothing $ merged == mempty
where
newRootOrEmpty x y =
if x == mempty && y /= mempty
then mkNewEmptyMutRoot
else x

mkNewEmptyMutRoot :: VT.ObjTyInfo
mkNewEmptyMutRoot = VT.ObjTyInfo (Just "mutation root")
(G.NamedType "mutation_root") Set.empty Map.empty

mkNewMutRoot :: VT.ObjFieldMap -> VT.ObjTyInfo
mkNewMutRoot flds = VT.ObjTyInfo (Just "mutation root")
(G.NamedType "mutation_root") Set.empty flds
mergeMutRoot a b = GS._gMutRoot a <> GS._gMutRoot b

mergeSubRoot :: GS.GCtx -> GS.GCtx -> Maybe VT.ObjTyInfo
mergeSubRoot a b =
let objA' = fromMaybe mempty $ GS._gSubRoot a
objB = fromMaybe mempty $ GS._gSubRoot b
objA = newRootOrEmpty objA' objB
merged = objA <> objB
in bool (Just merged) Nothing $ merged == mempty
where
newRootOrEmpty x y =
if x == mempty && y /= mempty
then mkNewEmptySubRoot
else x

mkNewEmptySubRoot :: VT.ObjTyInfo
mkNewEmptySubRoot = VT.ObjTyInfo (Just "subscription root")
(G.NamedType "subscription_root") Set.empty Map.empty

mergeSubRoot a b = GS._gSubRoot a <> GS._gSubRoot b

mergeTyMaps
:: VT.TypeMap
Expand Down
2 changes: 1 addition & 1 deletion server/src-lib/Hasura/GraphQL/Resolve/Action.hs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ resolveAsyncActionQuery userInfo selectOpCtx field = do

annotatedFields <- fmap (map (first FieldName)) $ withSelSet (_fSelSet field) $ \fld ->
case _fName fld of
"__typename" -> return $ RS.FExp $ G.unName $ G.unNamedType $ _fType fld
"__typename" -> return $ RS.FExp $ G.unName $ G.unNamedType $ _fType field
"output" -> do
-- See Note [Resolving async action query/subscription]
let inputTableArgument = RS.AETableRow $ Just $ Iden "response_payload"
Expand Down
9 changes: 7 additions & 2 deletions server/src-lib/Hasura/GraphQL/Resolve/Introspect.hs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ objectTypeR
=> ObjTyInfo
-> Field
-> m J.Object
objectTypeR (ObjTyInfo descM n iFaces flds) fld =
objectTypeR objectType fld =
withSubFields (_fSelSet fld) $ \subFld ->
case _fName subFld of
"__typename" -> retJT "__Type"
Expand All @@ -87,6 +87,11 @@ objectTypeR (ObjTyInfo descM n iFaces flds) fld =
sortOn _fiName $
filter notBuiltinFld $ Map.elems flds
_ -> return J.Null
where
descM = _otiDesc objectType
n = _otiName objectType
iFaces = _otiImplIFaces objectType
flds = _otiFields objectType

notBuiltinFld :: ObjFldInfo -> Bool
notBuiltinFld f =
Expand Down Expand Up @@ -209,7 +214,7 @@ nonNullR gTyp fld =
"ofType" -> case gTyp of
G.TypeNamed (G.Nullability False) nt -> J.toJSON <$> namedTypeR nt subFld
G.TypeList (G.Nullability False) lt -> J.toJSON <$> listTypeR lt subFld
_ -> throw500 "nullable type passed to nonNullR"
_ -> throw500 "nullable type passed to nonNullR"
_ -> return J.Null

namedTypeR
Expand Down
11 changes: 5 additions & 6 deletions server/src-lib/Hasura/GraphQL/Schema/CustomTypes.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ import qualified Hasura.GraphQL.Validate.Types as VT

buildObjectTypeInfo :: RoleName -> AnnotatedObjectType -> VT.ObjTyInfo
buildObjectTypeInfo roleName annotatedObjectType =
VT.ObjTyInfo
{ VT._otiDesc = _otdDescription objectDefinition
, VT._otiName = unObjectTypeName $ _otdName objectDefinition
, VT._otiImplIFaces = mempty
, VT._otiFields = mapFromL VT._fiName $ fields <> catMaybes relationships
}
let description = _otdDescription objectDefinition
namedType = unObjectTypeName $ _otdName objectDefinition
fieldMap = mapFromL VT._fiName $ fields <> catMaybes relationships
-- 'mkObjTyInfo' function takes care of inserting `__typename` field
in VT.mkObjTyInfo description namedType mempty fieldMap VT.TLCustom
where
objectDefinition = _aotDefinition annotatedObjectType

Expand Down
8 changes: 7 additions & 1 deletion server/src-lib/Hasura/GraphQL/Validate/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ module Hasura.GraphQL.Validate.Types
, mkHsraObjFldInfo
, ObjFieldMap

, ObjTyInfo(..)
-- Don't expose 'ObjTyInfo' constructor. Instead use 'mkObjTyInfo' or 'mkHsraObjTyInfo'
-- which will auto-insert the compulsory '__typename' field.
, ObjTyInfo
, _otiDesc
, _otiName
, _otiImplIFaces
, _otiFields
, mkObjTyInfo
, mkHsraObjTyInfo

Expand Down
4 changes: 4 additions & 0 deletions server/tests-py/queries/actions/sync/create_user_success.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ query:
query: |
mutation {
create_user(email: "clarke@gmail.com", name: "Clarke"){
__typename
id
user {
__typename
name
email
is_admin
Expand All @@ -17,8 +19,10 @@ query:
response:
data:
create_user:
__typename: UserId
id: 1
user:
__typename: user
name: Clarke
email: clarke@gmail.com
is_admin: false
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ query:
mutation {
create_users(users: [{email: "blake@hasura.io", name: "Blake"}, {email: "random-email", name: "Elsa"}]){
id
__typename
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ query:
mutation {
create_users(users: [{email: "blake@hasura.io", name: "Blake"}, {email: "elsa@hasura.io", name: "Elsa"}]){
id
__typename
user {
__typename
name
email
is_admin
Expand All @@ -18,12 +20,16 @@ response:
data:
create_users:
- id: 1
__typename: UserId
user:
__typename: user
name: Blake
email: blake@hasura.io
is_admin: false
- id: 2
__typename: UserId
user:
__typename: user
name: Elsa
email: elsa@hasura.io
is_admin: false
6 changes: 6 additions & 0 deletions server/tests-py/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,13 @@ def test_create_user_success(self, hge_ctx):
query_async = '''
query ($action_id: uuid!){
create_user(id: $action_id){
__typename
id
output {
__typename
id
user {
__typename
name
email
is_admin
Expand All @@ -148,10 +151,13 @@ def test_create_user_success(self, hge_ctx):
response = {
'data': {
'create_user': {
'__typename': 'create_user',
'id': action_id,
'output': {
'__typename': 'UserId',
'id': 1,
'user': {
'__typename': 'user',
'name': 'Clarke',
'email': 'clarke@hasura.io',
'is_admin': False
Expand Down

0 comments on commit 996ce92

Please sign in to comment.