@@ -6,13 +6,14 @@ use graphql_parser::{
66 schema:: { Definition , TypeDefinition } ,
77} ;
88use juniper:: {
9- meta:: { Field , MetaType } ,
9+ marker:: { IsOutputType , IsInputType } ,
10+ meta:: { Field , MetaType , Argument } ,
11+ types:: base:: resolve_selection_set_into,
1012 Arguments , ExecutionResult , Executor , FieldError , GraphQLType , GraphQLValue , Registry ,
11- ScalarValue , Selection , Value , GraphQLValueAsync , BoxFuture ,
13+ ScalarValue , Selection , Value , GraphQLValueAsync , BoxFuture , FromInputValue , InputValue ,
1214} ;
1315use serde_json:: Value as Json ;
1416
15- use crate :: { types:: base:: resolve_selection_set_into} ;
1617
1718// Used to describe the graphql type of a `serde_json::Value` using the GraphQL
1819// schema definition language.
@@ -33,8 +34,10 @@ impl TypeInfo {
3334 S : ScalarValue + ' r ,
3435 {
3536 let mut fields = Vec :: new ( ) ;
37+ let mut input_fields = Vec :: new ( ) ;
3638 let s = self . schema . clone ( ) . unwrap_or_default ( ) ;
3739 let ast = parse_schema :: < & str > ( s. as_str ( ) ) . unwrap ( ) ;
40+ let mut is_input_object = false ;
3841 for d in & ast. definitions {
3942 match & d {
4043 Definition :: TypeDefinition ( d) => match d {
@@ -50,14 +53,40 @@ impl TypeInfo {
5053 }
5154 }
5255 }
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+ }
5376 _ => todo ! ( ) ,
5477 } ,
5578 _ => { }
5679 }
5780 }
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+ }
6190 }
6291
6392 fn build_field < ' r , ' t , S , T > (
@@ -142,6 +171,54 @@ impl<S: ScalarValue> GraphQLType<S> for Json {
142171 }
143172}
144173
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+
145222impl < S : ScalarValue > GraphQLValue < S > for Json {
146223 type Context = ( ) ;
147224 type TypeInfo = TypeInfo ;
@@ -264,11 +341,15 @@ impl<S> GraphQLValueAsync<S> for Json
264341#[ cfg( test) ]
265342mod tests {
266343 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 ,
268350 } ;
269351 use serde_json:: json;
270352
271- use super :: TypeInfo ;
272353
273354 #[ test]
274355 fn sdl_type_info ( ) {
@@ -503,5 +584,157 @@ mod tests {
503584 ) ) ,
504585 ) ;
505586 }
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+ }
506739}
507740
0 commit comments