25
25
GraphQLNonNull ,
26
26
GraphQLObjectType ,
27
27
GraphQLSchema ,
28
+ GraphQLString ,
28
29
GraphQLWrappingType ,
29
30
InlineFragmentNode ,
30
31
IntValueNode ,
46
47
VariableDefinitionNode ,
47
48
VariableNode ,
48
49
assert_named_type ,
50
+ introspection_types ,
49
51
is_enum_type ,
50
52
is_input_object_type ,
51
53
is_leaf_type ,
@@ -301,6 +303,7 @@ class DSLExecutable(ABC):
301
303
302
304
variable_definitions : "DSLVariableDefinitions"
303
305
name : Optional [str ]
306
+ selection_set : SelectionSetNode
304
307
305
308
@property
306
309
@abstractmethod
@@ -349,11 +352,31 @@ def __init__(
349
352
f"Received type: { type (field )} "
350
353
)
351
354
)
355
+ valid_type = False
352
356
if isinstance (self , DSLOperation ):
353
- assert field .type_name .upper () == self .operation_type .name , (
354
- f"Invalid root field for operation { self .operation_type .name } .\n "
355
- f"Received: { field .type_name } "
356
- )
357
+ operation_name = self .operation_type .name
358
+ if isinstance (field , DSLMetaField ):
359
+ if field .name in ["__schema" , "__type" ]:
360
+ valid_type = operation_name == "QUERY"
361
+ if field .name == "__typename" :
362
+ valid_type = operation_name != "SUBSCRIPTION"
363
+ else :
364
+ valid_type = field .parent_type .name .upper () == operation_name
365
+
366
+ else : # Fragments
367
+ if isinstance (field , DSLMetaField ):
368
+ valid_type = field .name == "__typename"
369
+
370
+ if not valid_type :
371
+ if isinstance (self , DSLOperation ):
372
+ error_msg = (
373
+ "Invalid root field for operation "
374
+ f"{ self .operation_type .name } "
375
+ )
376
+ else :
377
+ error_msg = f"Invalid field for fragment { self .name } "
378
+
379
+ raise AssertionError (f"{ error_msg } : { field !r} " )
357
380
358
381
self .selection_set = SelectionSetNode (
359
382
selections = FrozenList (DSLSelectable .get_ast_fields (all_fields ))
@@ -610,6 +633,11 @@ def select(
610
633
fields , fields_with_alias
611
634
)
612
635
636
+ # Check that we don't receive an invalid meta-field
637
+ for field in added_fields :
638
+ if isinstance (field , DSLMetaField ) and field .name != "__typename" :
639
+ raise AssertionError (f"Invalid field for { self !r} : { field !r} " )
640
+
613
641
# Get a list of AST Nodes for each added field
614
642
added_selections : List [
615
643
Union [FieldNode , InlineFragmentNode , FragmentSpreadNode ]
@@ -668,8 +696,8 @@ class DSLField(DSLSelectableWithAlias, DSLSelector):
668
696
def __init__ (
669
697
self ,
670
698
name : str ,
671
- graphql_type : Union [GraphQLObjectType , GraphQLInterfaceType ],
672
- graphql_field : GraphQLField ,
699
+ parent_type : Union [GraphQLObjectType , GraphQLInterfaceType ],
700
+ field : GraphQLField ,
673
701
):
674
702
"""Initialize the DSLField.
675
703
@@ -678,15 +706,21 @@ def __init__(
678
706
Use attributes of the :class:`DSLType` instead.
679
707
680
708
:param name: the name of the field
681
- :param graphql_type: the GraphQL type definition from the schema
682
- :param graphql_field: the GraphQL field definition from the schema
709
+ :param parent_type: the GraphQL type definition from the schema of the
710
+ parent type of the field
711
+ :param field: the GraphQL field definition from the schema
683
712
"""
684
713
DSLSelector .__init__ (self )
685
- self ._type = graphql_type
686
- self .field = graphql_field
714
+ self .parent_type = parent_type
715
+ self .field = field
687
716
self .ast_field = FieldNode (name = NameNode (value = name ), arguments = FrozenList ())
688
717
log .debug (f"Creating { self !r} " )
689
718
719
+ @property
720
+ def name (self ):
721
+ """:meta private:"""
722
+ return self .ast_field .name .value
723
+
690
724
def __call__ (self , ** kwargs ) -> "DSLField" :
691
725
return self .args (** kwargs )
692
726
@@ -750,16 +784,49 @@ def select(
750
784
751
785
return self
752
786
753
- @property
754
- def type_name (self ):
755
- """:meta private:"""
756
- return self ._type .name
757
-
758
787
def __repr__ (self ) -> str :
759
- return (
760
- f"<{ self .__class__ .__name__ } { self ._type .name } "
761
- f"::{ self .ast_field .name .value } >"
762
- )
788
+ return f"<{ self .__class__ .__name__ } { self .parent_type .name } " f"::{ self .name } >"
789
+
790
+
791
+ class DSLMetaField (DSLField ):
792
+ """DSLMetaField represents a GraphQL meta-field for the DSL code.
793
+
794
+ meta-fields are reserved field in the GraphQL type system prefixed with
795
+ "__" two underscores and used for introspection.
796
+ """
797
+
798
+ meta_type = GraphQLObjectType (
799
+ "meta-field" ,
800
+ fields = {
801
+ "__typename" : GraphQLField (GraphQLString ),
802
+ "__schema" : GraphQLField (
803
+ cast (GraphQLObjectType , introspection_types ["__Schema" ])
804
+ ),
805
+ "__type" : GraphQLField (
806
+ cast (GraphQLObjectType , introspection_types ["__Type" ]),
807
+ args = {"name" : GraphQLArgument (type_ = GraphQLNonNull (GraphQLString ))},
808
+ ),
809
+ },
810
+ )
811
+
812
+ def __init__ (self , name : str ):
813
+ """Initialize the meta-field.
814
+
815
+ :param name: the name between __typename, __schema or __type
816
+ """
817
+
818
+ try :
819
+ field = self .meta_type .fields [name ]
820
+ except KeyError :
821
+ raise AssertionError (f'Invalid meta-field "{ name } "' )
822
+
823
+ super ().__init__ (name , self .meta_type , field )
824
+
825
+ def alias (self , alias : str ) -> "DSLSelectableWithAlias" :
826
+ """
827
+ :meta private:
828
+ """
829
+ pass
763
830
764
831
765
832
class DSLInlineFragment (DSLSelectable , DSLSelector ):
0 commit comments