@@ -202,14 +202,27 @@ def add_reverse_edges(schema: GraphQLSchema):
202
202
# Reverse edge
203
203
edge_from = get_named_type (field_type .type )
204
204
edge_name = f'_{ field_name } From{ _type .name } '
205
- edge_to = GraphQLList (_type )
205
+
206
+ directives = {}
207
+ directive_to_add = ''
208
+
209
+ if hasattr (field_type , 'ast_node' ) and field_type .ast_node is not None :
210
+ directives = {directive .name .value : directive for directive in field_type .ast_node .directives }
211
+
212
+ if 'requiredForTarget' in directives :
213
+ directive_to_add = '@required'
214
+
215
+ if 'uniqueForTarget' in directives :
216
+ edge_to = _type
217
+ else :
218
+ edge_to = GraphQLList (_type )
206
219
207
220
if is_interface_type (edge_from ):
208
- make += 'extend interface {0} {{ {1}: {2} }} \n ' .format (edge_from , edge_name , edge_to )
221
+ make += 'extend interface {0} {{ {1}: {2} {3} }} \n ' .format (edge_from , edge_name , edge_to , directive_to_add )
209
222
for implementing_type in schema .get_possible_types (edge_from ):
210
223
make += 'extend type {0} {{ {1}: {2} }}\n ' .format (implementing_type , edge_name , edge_to )
211
224
else :
212
- make += 'extend type {0} {{ {1}: {2} }} \n ' .format (edge_from , edge_name , edge_to )
225
+ make += 'extend type {0} {{ {1}: {2} {3} }} \n ' .format (edge_from , edge_name , edge_to , directive_to_add )
213
226
schema = add_to_schema (schema , make )
214
227
215
228
return schema
@@ -751,3 +764,197 @@ def add_delete_mutations(schema: GraphQLSchema):
751
764
make += f'extend type Mutation {{ { delete } (id: ID!): { _type .name } }} '
752
765
schema = add_to_schema (schema , make )
753
766
return schema
767
+
768
+
769
+ def ast_type_to_string (_type : GraphQLType ):
770
+ """
771
+ Print the ast_type properly
772
+ :param _type:
773
+ :return:
774
+ """
775
+
776
+ # ast_nodes types behavies differnetly than for other types (as they are NodeTypes)
777
+ # So we can't use the normal functions
778
+
779
+
780
+ _post_str = ''
781
+ _pre_str = ''
782
+ # A, A!, [A!], [A]!, [A!]!
783
+ wrappers = []
784
+ if isinstance (_type , NonNullTypeNode ):
785
+ _post_str = '!'
786
+ _type = _type .type
787
+ if isinstance (_type , ListTypeNode ):
788
+ _post_str = ']' + _post_str
789
+ _pre_str = '['
790
+ _type = _type .type
791
+ if isinstance (_type , NonNullTypeNode ):
792
+ _post_str = '!' + _post_str
793
+ _type = _type .type
794
+
795
+ # Dig down to find the actual named node, should be the first one actually
796
+ name = _type
797
+ while not isinstance (name , NamedTypeNode ):
798
+ name = name .type
799
+ name = name .name .value
800
+
801
+ return _pre_str + name + _post_str
802
+
803
+
804
+ def directive_from_interface (directive , interface_name ):
805
+ """
806
+ Return the correct directive string fro directives inhertied from interfaces
807
+ :param directive:
808
+ :param interface_name:
809
+ :return string:
810
+ """
811
+ directive_string = directive .name .value
812
+
813
+ # The only two cases who needs special attention is @requiredForTarget and @uniqueForTarget
814
+ if directive_string == 'requiredForTarget' :
815
+ directive_string = '_requiredForTarget_AccordingToInterface(interface: "' + interface_name + '")'
816
+ elif directive_string == 'uniqueForTarget' :
817
+ directive_string = '_uniqueForTarget_AccordingToInterface(interface: "' + interface_name + '")'
818
+ else :
819
+ directive_string += get_directive_arguments (directive )
820
+
821
+ return directive_string
822
+
823
+
824
+ def get_directive_arguments (directive ):
825
+ """
826
+ Get the arguments of the given directive as string
827
+ :param directive:
828
+ :return string:
829
+ """
830
+
831
+ output = ''
832
+ if len (directive .arguments ) > 0 :
833
+ output += '('
834
+ for arg in directive .arguments :
835
+ output += arg .name .value + ':'
836
+ if isinstance (arg .value , ListValueNode ):
837
+ output += '['
838
+ for V in arg .value .values :
839
+ if isinstance (V , StringValueNode ):
840
+ output += '"' + V .value + '", '
841
+ else :
842
+ output += V .value + ', '
843
+
844
+ output = output [:- 2 ] + ']'
845
+
846
+ else :
847
+ if isinstance (arg .value , StringValueNode ):
848
+ output += '"' + arg .value .value + '", '
849
+ else :
850
+ output += arg .value .value + ', '
851
+
852
+ output += ', '
853
+
854
+ output = output [:- 2 ] + ')'
855
+
856
+ return output
857
+
858
+
859
+ def printSchemaWithDirectives (schema ):
860
+
861
+ output = ''
862
+
863
+ for _dir in schema .directives :
864
+ if _dir .ast_node is not None :
865
+ # If the directive does not have a proper ast_node
866
+ # Then it is an non-user defined directive, and can hence, be skipped
867
+ output += 'directive @' + _dir .name
868
+
869
+ if len (_dir .ast_node .arguments ) > 0 :
870
+ output += '('
871
+ for arg in _dir .ast_node .arguments :
872
+ output += arg .name .value + ': ' + ast_type_to_string (arg .type ) + ', '
873
+ output = output [:- 2 ] + ')'
874
+
875
+ output += ' on '
876
+ for _location in _dir .locations :
877
+ output += _location ._name_ + ', '
878
+
879
+ output = output [:- 2 ] + '\n \n '
880
+
881
+
882
+ output += 'directive @_requiredForTarget_AccordingToInterface(interface: String!) on FIELD_DEFINITION\n \n '
883
+ output += 'directive @_uniqueForTarget_AccordingToInterface(interface: String!) on FIELD_DEFINITION\n \n '
884
+
885
+
886
+ for _type in sorted (schema .type_map .values (), key = lambda x : x .name ):
887
+ if _type .name [:2 ] == '__' :
888
+ continue
889
+
890
+ if is_interface_type (_type ):
891
+ output += 'interface ' + _type .name
892
+ elif is_enum_type (_type ):
893
+ output += 'enum ' + _type .name
894
+ elif is_scalar_type (_type ):
895
+ if _type .ast_node is not None :
896
+ # If the scalar does not have a proper ast_node
897
+ # Then it is an non-user defined scalar, and can hence, be skipped
898
+ output += 'scalar ' + _type .name
899
+ elif is_input_type (_type ):
900
+ output += 'input ' + _type .name
901
+ else : # type, hopefully
902
+ output += 'type ' + _type .name
903
+ if hasattr (_type , 'interfaces' ) and len (_type .interfaces ) > 0 :
904
+ output += ' implements '
905
+ for interface in _type .interfaces :
906
+ output += interface .name + ', '
907
+ output = output [:- 2 ]
908
+
909
+ if is_enum_type (_type ):
910
+ output += ' {\n '
911
+ for value in _type .values :
912
+ output += ' ' + value + '\n '
913
+ output += '}'
914
+
915
+ elif not is_enum_or_scalar (_type ):
916
+ if _type .ast_node is not None :
917
+ for directive in _type .ast_node .directives :
918
+ output += ' @' + directive .name .value
919
+ output += get_directive_arguments (directive )
920
+
921
+ output += ' {\n '
922
+
923
+ for field_name , field in _type .fields .items ():
924
+ output += ' ' + field_name
925
+
926
+ if hasattr (field , 'args' ) and field .args :
927
+ output += '('
928
+ for arg_name , arg in field .args .items ():
929
+ output += arg_name + ': ' + str (arg .type ) + ', '
930
+ output = output [:- 2 ] + ')'
931
+
932
+ output += ': ' + str (field .type )
933
+
934
+ directives_set = set ()
935
+
936
+ for directive in field .ast_node .directives :
937
+ if not directive .name .value in directives_set :
938
+ output += ' @' + directive .name .value
939
+ directives_set .add (directive .name .value )
940
+ output += get_directive_arguments (directive )
941
+
942
+
943
+ if hasattr (_type , 'interfaces' ):
944
+ for interface in _type .interfaces :
945
+ if field_name in interface .fields :
946
+ for directive in interface .fields [field_name ].ast_node .directives :
947
+ directive_str = directive_from_interface (directive , interface .name )
948
+ if not directive_str in directives_set :
949
+ output += ' @' + directive_str
950
+ directives_set .add (directive_str )
951
+
952
+
953
+ output += '\n '
954
+
955
+ output += '}'
956
+
957
+ if _type .ast_node is not None :
958
+ output += '\n \n '
959
+
960
+ return output
0 commit comments