@@ -6,13 +6,14 @@ use graphql_parser::{
6
6
schema:: { Definition , TypeDefinition } ,
7
7
} ;
8
8
use juniper:: {
9
- meta:: { Field , MetaType } ,
9
+ marker:: { IsOutputType , IsInputType } ,
10
+ meta:: { Field , MetaType , Argument } ,
11
+ types:: base:: resolve_selection_set_into,
10
12
Arguments , ExecutionResult , Executor , FieldError , GraphQLType , GraphQLValue , Registry ,
11
- ScalarValue , Selection , Value , GraphQLValueAsync , BoxFuture ,
13
+ ScalarValue , Selection , Value , GraphQLValueAsync , BoxFuture , FromInputValue , InputValue ,
12
14
} ;
13
15
use serde_json:: Value as Json ;
14
16
15
- use crate :: { types:: base:: resolve_selection_set_into} ;
16
17
17
18
// Used to describe the graphql type of a `serde_json::Value` using the GraphQL
18
19
// schema definition language.
@@ -33,8 +34,10 @@ impl TypeInfo {
33
34
S : ScalarValue + ' r ,
34
35
{
35
36
let mut fields = Vec :: new ( ) ;
37
+ let mut input_fields = Vec :: new ( ) ;
36
38
let s = self . schema . clone ( ) . unwrap_or_default ( ) ;
37
39
let ast = parse_schema :: < & str > ( s. as_str ( ) ) . unwrap ( ) ;
40
+ let mut is_input_object = false ;
38
41
for d in & ast. definitions {
39
42
match & d {
40
43
Definition :: TypeDefinition ( d) => match d {
@@ -50,14 +53,40 @@ impl TypeInfo {
50
53
}
51
54
}
52
55
}
56
+ TypeDefinition :: InputObject ( d) => {
57
+ if d. name == self . name {
58
+ is_input_object = true ;
59
+ for field in & d. fields {
60
+ let f = self . build_field (
61
+ registry,
62
+ field. name ,
63
+ field. value_type . clone ( ) ,
64
+ true ,
65
+ ) ;
66
+
67
+ input_fields. push ( Argument {
68
+ name : field. name . to_string ( ) ,
69
+ description : field. description . clone ( ) ,
70
+ arg_type : f. field_type ,
71
+ default_value : None ,
72
+ } ) ;
73
+ }
74
+ }
75
+ }
53
76
_ => todo ! ( ) ,
54
77
} ,
55
78
_ => { }
56
79
}
57
80
}
58
- registry
59
- . build_object_type :: < Json > ( self , & fields)
60
- . into_meta ( )
81
+ if is_input_object {
82
+ registry
83
+ . build_input_object_type :: < Json > ( self , & input_fields)
84
+ . into_meta ( )
85
+ } else {
86
+ registry
87
+ . build_object_type :: < Json > ( self , & fields)
88
+ . into_meta ( )
89
+ }
61
90
}
62
91
63
92
fn build_field < ' r , ' t , S , T > (
@@ -142,6 +171,54 @@ impl<S: ScalarValue> GraphQLType<S> for Json {
142
171
}
143
172
}
144
173
174
+ impl < S > IsOutputType < S > for Json where S : ScalarValue { }
175
+
176
+ impl < S > IsInputType < S > for Json where S : ScalarValue { }
177
+
178
+ impl < S > FromInputValue < S > for Json where S : ScalarValue
179
+ {
180
+ fn from_input_value ( v : & InputValue < S > ) -> Option < Self > {
181
+ match v {
182
+ InputValue :: Null => {
183
+ Some ( Json :: Null )
184
+ }
185
+ InputValue :: Scalar ( x) => {
186
+ Some ( if let Some ( i) = x. as_int ( ) {
187
+ Json :: Number ( serde_json:: Number :: from ( i) )
188
+ } else if let Some ( f) = x. as_float ( ) {
189
+ Json :: Number ( serde_json:: Number :: from_f64 ( f) . expect ( "f64 to convert" ) )
190
+ } else if let Some ( b) = x. as_boolean ( ) {
191
+ Json :: Bool ( b)
192
+ } else if let Some ( s) = x. as_str ( ) {
193
+ Json :: String ( s. to_string ( ) )
194
+ } else {
195
+ unreachable ! ( "`ScalarValue` must represent at least one of the GraphQL spec types" )
196
+ } )
197
+ }
198
+ InputValue :: Enum ( x) => {
199
+ Some ( Json :: String ( x. clone ( ) ) )
200
+ }
201
+ InputValue :: List ( ls) => {
202
+ let v: Vec < Json > = ls. iter ( ) . filter_map ( |i| i. item . convert ( ) ) . collect ( ) ;
203
+ Some ( Json :: Array ( v) )
204
+ }
205
+ InputValue :: Object ( fields) => {
206
+ let mut obj = serde_json:: Map :: new ( ) ;
207
+ for field in fields {
208
+ let v: Option < Json > = field. 1 . item . convert ( ) ;
209
+ if let Some ( v) = v {
210
+ obj. insert ( field. 0 . item . clone ( ) , v) ;
211
+ }
212
+ }
213
+ Some ( Json :: Object ( obj) )
214
+ }
215
+ InputValue :: Variable ( _) => {
216
+ None
217
+ }
218
+ }
219
+ }
220
+ }
221
+
145
222
impl < S : ScalarValue > GraphQLValue < S > for Json {
146
223
type Context = ( ) ;
147
224
type TypeInfo = TypeInfo ;
@@ -264,11 +341,15 @@ impl<S> GraphQLValueAsync<S> for Json
264
341
#[ cfg( test) ]
265
342
mod tests {
266
343
use juniper:: {
267
- execute_sync, graphql_value, EmptyMutation , EmptySubscription , RootNode , Variables ,
344
+ marker:: { IsOutputType , IsInputType } ,
345
+ meta:: MetaType ,
346
+ integrations:: json:: TypeInfo ,
347
+ execute_sync, graphql_object, graphql_value, EmptyMutation , EmptySubscription , RootNode , Variables ,
348
+ ScalarValue , GraphQLValue , GraphQLType , Selection , Executor , ExecutionResult , FieldResult ,
349
+ GraphQLValueAsync , Registry , ToInputValue , FromInputValue , InputValue ,
268
350
} ;
269
351
use serde_json:: json;
270
352
271
- use super :: TypeInfo ;
272
353
273
354
#[ test]
274
355
fn sdl_type_info ( ) {
@@ -503,5 +584,157 @@ mod tests {
503
584
) ) ,
504
585
) ;
505
586
}
587
+
588
+ #[ test]
589
+ fn test_as_field_of_output_type ( ) {
590
+ // We need a Foo wrapper associate a static SDL to the Foo type which
591
+ // wraps the serde_json::Value. Would be nice if a macro could code gen this.
592
+ struct Foo ( serde_json:: Value ) ;
593
+ impl < S > IsOutputType < S > for Foo where S : ScalarValue { }
594
+ impl < S > GraphQLValueAsync < S > for Foo where S : ScalarValue + Send + Sync { }
595
+ impl < S > GraphQLType < S > for Foo where S : ScalarValue
596
+ {
597
+ fn name ( _info : & Self :: TypeInfo ) -> Option < & str > {
598
+ Some ( "Foo" )
599
+ }
600
+ fn meta < ' r > ( _info : & Self :: TypeInfo , registry : & mut Registry < ' r , S > ) -> MetaType < ' r , S >
601
+ where S : ' r ,
602
+ {
603
+ TypeInfo {
604
+ name : "Foo" . to_string ( ) ,
605
+ schema : Some ( r#"
606
+ type Foo {
607
+ message: [String]
608
+ }
609
+ "# . to_string ( ) ) ,
610
+ } . meta ( registry)
611
+ }
612
+ }
613
+ impl < S > GraphQLValue < S > for Foo where S : ScalarValue
614
+ {
615
+ type Context = ( ) ;
616
+ type TypeInfo = ( ) ;
617
+ fn type_name < ' i > ( & self , info : & ' i Self :: TypeInfo ) -> Option < & ' i str > {
618
+ <Self as GraphQLType >:: name ( info)
619
+ }
620
+ fn resolve (
621
+ & self ,
622
+ _info : & Self :: TypeInfo ,
623
+ _selection : Option < & [ Selection < S > ] > ,
624
+ executor : & Executor < Self :: Context , S > ,
625
+ ) -> ExecutionResult < S > {
626
+ executor. resolve ( & TypeInfo { schema : None , name : "Foo" . to_string ( ) } , & self . 0 )
627
+ }
628
+ }
629
+
630
+ struct Query ;
631
+ #[ graphql_object( ) ]
632
+ impl Query {
633
+ fn foo ( ) -> FieldResult < Foo > {
634
+ let data = json ! ( { "message" : [ "Hello" , "World" ] } ) ;
635
+ Ok ( Foo ( data) )
636
+ }
637
+ }
638
+ let schema = juniper:: RootNode :: new ( Query , EmptyMutation :: new ( ) , EmptySubscription :: new ( ) ) ;
639
+ // Run the executor.
640
+ let ( res, _errors) = juniper:: execute_sync (
641
+ "query { foo { message } }" ,
642
+ None ,
643
+ & schema,
644
+ & Variables :: new ( ) ,
645
+ & ( ) ,
646
+ ) . unwrap ( ) ;
647
+
648
+ // Ensure the value matches.
649
+ assert_eq ! (
650
+ res,
651
+ graphql_value!( {
652
+ "foo" : { "message" : [ "Hello" , "World" ] } ,
653
+ } )
654
+ ) ;
655
+ }
656
+
657
+
658
+ #[ test]
659
+ fn test_as_field_of_input_type ( ) {
660
+ // We need a Foo wrapper associate a static SDL to the Foo type which
661
+ // wraps the serde_json::Value. Would be nice if a macro could code gen this.
662
+
663
+ #[ derive( Debug , Clone , PartialEq ) ]
664
+ struct Foo ( serde_json:: Value ) ;
665
+ impl < S > IsInputType < S > for Foo where S : ScalarValue { }
666
+ impl < S > GraphQLValueAsync < S > for Foo where S : ScalarValue + Send + Sync { }
667
+ impl < S > FromInputValue < S > for Foo where S : ScalarValue {
668
+ fn from_input_value ( v : & InputValue < S > ) -> Option < Self > {
669
+ <serde_json:: Value as FromInputValue < S > >:: from_input_value ( v) . map ( |x| Foo ( x) )
670
+ }
671
+ }
672
+ impl < S > GraphQLType < S > for Foo where S : ScalarValue
673
+ {
674
+ fn name ( _info : & Self :: TypeInfo ) -> Option < & str > {
675
+ Some ( "Foo" )
676
+ }
677
+ fn meta < ' r > ( _info : & Self :: TypeInfo , registry : & mut Registry < ' r , S > ) -> MetaType < ' r , S >
678
+ where S : ' r ,
679
+ {
680
+ TypeInfo {
681
+ name : "Foo" . to_string ( ) ,
682
+ schema : Some ( r#"
683
+ input Foo {
684
+ message: [String]
685
+ }
686
+ "# . to_string ( ) ) ,
687
+ } . meta ( registry)
688
+ }
689
+ }
690
+ impl < S > GraphQLValue < S > for Foo where S : ScalarValue
691
+ {
692
+ type Context = ( ) ;
693
+ type TypeInfo = ( ) ;
694
+ fn type_name < ' i > ( & self , info : & ' i Self :: TypeInfo ) -> Option < & ' i str > {
695
+ <Self as GraphQLType >:: name ( info)
696
+ }
697
+ fn resolve (
698
+ & self ,
699
+ _info : & Self :: TypeInfo ,
700
+ _selection : Option < & [ Selection < S > ] > ,
701
+ executor : & Executor < Self :: Context , S > ,
702
+ ) -> ExecutionResult < S > {
703
+ executor. resolve ( & TypeInfo { schema : None , name : "Foo" . to_string ( ) } , & self . 0 )
704
+ }
705
+ }
706
+
707
+ struct Query ;
708
+ #[ graphql_object( ) ]
709
+ impl Query {
710
+ fn foo ( value : Foo ) -> FieldResult < bool > {
711
+ Ok ( value == Foo ( json ! ( { "message" : [ "Hello" , "World" ] } ) ) )
712
+ }
713
+ }
714
+ let schema = juniper:: RootNode :: new ( Query , EmptyMutation :: new ( ) , EmptySubscription :: new ( ) ) ;
715
+
716
+ let vars = vec ! [ ( "value" . to_owned( ) , graphql_value!( {
717
+ "message" : [ "Hello" , "World" ] ,
718
+ } ) . to_input_value( ) ) ]
719
+ . into_iter ( )
720
+ . collect ( ) ;
721
+
722
+ // Run the executor.
723
+ let ( res, _errors) = juniper:: execute_sync (
724
+ "query example($value:Foo!){ foo(value: $value) }" ,
725
+ None ,
726
+ & schema,
727
+ & vars,
728
+ & ( ) ,
729
+ ) . unwrap ( ) ;
730
+
731
+ // Ensure the value matches.
732
+ assert_eq ! (
733
+ res,
734
+ graphql_value!( {
735
+ "foo" : true ,
736
+ } )
737
+ ) ;
738
+ }
506
739
}
507
740
0 commit comments