@@ -5,6 +5,7 @@ namespace FSharp.Data.GraphQL
55
66open System
77open System.Collections .Generic
8+ open System.Collections .Immutable
89open System.Net .Http
910open System.Text
1011open System.Threading
@@ -15,40 +16,42 @@ open FSharp.Data.GraphQL.Client
1516open ReflectionPatterns
1617
1718/// A requrest object for making GraphQL calls using the GraphQL client module.
18- type GraphQLRequest =
19- { /// Gets the URL of the GraphQL server which will be called.
20- ServerUrl : string
21- /// Gets custom HTTP Headers to pass with each call using this request.
22- HttpHeaders: seq < string * string >
23- /// Gets the name of the operation that should run on the server.
24- OperationName : string option
25- /// Gets the query string which should be executed on the GraphQL server.
26- Query : string
27- /// Gets variables to be sent with the query.
28- Variables : ( string * obj ) [] }
19+ type GraphQLRequest = {
20+ /// Gets the URL of the GraphQL server which will be called.
21+ ServerUrl : string
22+ /// Gets custom HTTP Headers to pass with each call using this request.
23+ HttpHeaders : seq < string * string >
24+ /// Gets the name of the operation that should run on the server.
25+ OperationName : string option
26+ /// Gets the query string which should be executed on the GraphQL server.
27+ Query : string
28+ /// Gets variables to be sent with the query.
29+ Variables : ( string * obj )[]
30+ }
2931
3032/// Executes calls to GraphQL servers and return their responses.
3133module GraphQLClient =
3234
3335 let private ensureSuccessCode ( response : Task < HttpResponseMessage >) = task {
3436 let! response = response
35- return response.EnsureSuccessStatusCode()
37+ return response.EnsureSuccessStatusCode ()
3638 }
3739
3840 let private addHeaders ( httpHeaders : seq < string * string >) ( requestMessage : HttpRequestMessage ) =
39- if not ( isNull httpHeaders)
40- then httpHeaders |> Seq.iter ( fun ( name , value ) -> requestMessage.Headers.Add( name, value))
41+ if not ( isNull httpHeaders) then
42+ httpHeaders
43+ |> Seq.iter ( fun ( name , value ) -> requestMessage.Headers.Add ( name, value))
4144
4245 let private postAsync ct ( invoker : HttpMessageInvoker ) ( serverUrl : string ) ( httpHeaders : seq < string * string >) ( content : HttpContent ) = task {
43- use requestMessage = new HttpRequestMessage( HttpMethod.Post, serverUrl)
46+ use requestMessage = new HttpRequestMessage ( HttpMethod.Post, serverUrl)
4447 requestMessage.Content <- content
4548 addHeaders httpHeaders requestMessage
46- return ! invoker.SendAsync( requestMessage, ct) |> ensureSuccessCode
49+ return ! invoker.SendAsync ( requestMessage, ct) |> ensureSuccessCode
4750 }
4851
4952 let private getAsync ct ( invoker : HttpMessageInvoker ) ( serverUrl : string ) = task {
50- use requestMessage = new HttpRequestMessage( HttpMethod.Get, serverUrl)
51- return ! invoker.SendAsync( requestMessage, ct) |> ensureSuccessCode
53+ use requestMessage = new HttpRequestMessage ( HttpMethod.Get, serverUrl)
54+ return ! invoker.SendAsync ( requestMessage, ct) |> ensureSuccessCode
5255 }
5356
5457 /// Sends a request to a GraphQL server asynchronously.
@@ -63,108 +66,129 @@ module GraphQLClient =
6366 | Some x -> JsonValue.String x
6467 | None -> JsonValue.Null
6568 let requestJson =
66- [| " operationName" , operationName
67- " query" , JsonValue.String request.Query
68- " variables" , variables |]
69+ [|
70+ " operationName" , operationName
71+ " query" , JsonValue.String request.Query
72+ " variables" , variables
73+ |]
6974 |> JsonValue.Record
70- let content = new StringContent( requestJson.ToString(), Encoding.UTF8, " application/json" )
75+ let content = new StringContent ( requestJson.ToString (), Encoding.UTF8, " application/json" )
7176 return ! postAsync ct invoker request.ServerUrl request.HttpHeaders content
7277 }
7378
7479 /// Sends a request to a GraphQL server.
75- let sendRequest client request = ( sendRequestAsync CancellationToken.None client request) .GetAwaiter() .GetResult()
80+ let sendRequest client request =
81+ ( sendRequestAsync CancellationToken.None client request) .GetAwaiter() .GetResult ()
7682
7783 /// Executes an introspection schema request to a GraphQL server asynchronously.
7884 let sendIntrospectionRequestAsync ct ( connection : GraphQLClientConnection ) ( serverUrl : string ) httpHeaders =
79- let sendGet () = getAsync ct connection.Invoker serverUrl
85+ let sendGet () = getAsync ct connection.Invoker serverUrl
8086 let rethrow ( exns : exn list ) =
8187 let rec mapper ( acc : string ) ( exns : exn list ) =
8288 let aggregateMapper ( ex : AggregateException ) = mapper " " ( List.ofSeq ex.InnerExceptions)
8389 match exns with
84- | [] -> acc.TrimEnd()
90+ | [] -> acc.TrimEnd ()
8591 | ex :: tail ->
8692 match ex with
8793 | :? AggregateException as ex -> mapper ( acc + aggregateMapper ex + " " ) tail
8894 | ex -> mapper ( acc + ex.Message + " " ) tail
8995 failwith $""" Failure trying to recover introspection schema from server at "%s {serverUrl}". Errors: %s {mapper "" exns}"""
9096 task {
91- try return ! sendGet()
97+ try
98+ return ! sendGet ()
9299 with getex ->
93- let request =
94- { ServerUrl = serverUrl
95- HttpHeaders = httpHeaders
96- OperationName = None
97- Query = IntrospectionQuery.Definition
98- Variables = [||] }
99- try return ! sendRequestAsync ct connection request
100- with postex -> return rethrow [ getex; postex]
100+ let request = {
101+ ServerUrl = serverUrl
102+ HttpHeaders = httpHeaders
103+ OperationName = None
104+ Query = IntrospectionQuery.Definition
105+ Variables = [||]
106+ }
107+ try
108+ return ! sendRequestAsync ct connection request
109+ with postex ->
110+ return rethrow [ getex; postex ]
101111 }
102112
103113 /// Executes an introspection schema request to a GraphQL server.
104114 let sendIntrospectionRequest client serverUrl httpHeaders =
105- ( sendIntrospectionRequestAsync CancellationToken.None client serverUrl httpHeaders) .GetAwaiter() .GetResult()
115+ ( sendIntrospectionRequestAsync CancellationToken.None client serverUrl httpHeaders) .GetAwaiter() .GetResult ()
106116
107117 /// Executes a multipart request to a GraphQL server asynchronously.
108118 let sendMultipartRequestAsync ct ( connection : GraphQLClientConnection ) ( request : GraphQLRequest ) = task {
109119 let invoker = connection.Invoker
110- let boundary = " ----GraphQLProviderBoundary" + ( Guid.NewGuid() .ToString( " N" ))
111- let content = new MultipartContent( " form-data" , boundary)
120+ let boundary =
121+ " ----GraphQLProviderBoundary"
122+ + ( Guid.NewGuid() .ToString ( " N" ))
123+ let content = new MultipartContent ( " form-data" , boundary)
112124 let files =
113- let rec tryMapFileVariable ( name : string , value : obj ) =
125+ let rec tryMapFileVariable ( name : string , value : obj ) =
114126 match value with
115- | null | : ? string -> None
116- | : ? Upload as x -> Some [| name , x |]
117- | OptionValue x ->
118- x |> Option.bind ( fun x -> tryMapFileVariable ( name, x))
127+ | null
128+ | : ? string -> None
129+ | : ? Upload as x -> Some [| name , x |]
130+ | OptionValue x -> x |> Option.bind ( fun x -> tryMapFileVariable ( name, x))
119131 | : ? IDictionary < string , obj > as x ->
120- x |> Seq.collect ( fun kvp -> tryMapFileVariable ( name + " ." + ( kvp.Key.FirstCharLower()), kvp.Value) |> Option.defaultValue [||])
121- |> Array.ofSeq
122- |> Some
132+ x
133+ |> Seq.collect ( fun kvp ->
134+ tryMapFileVariable ( name + " ." + ( kvp.Key.FirstCharLower ()), kvp.Value)
135+ |> Option.defaultValue [||])
136+ |> Array.ofSeq
137+ |> Some
123138 | EnumerableValue x ->
124- x |> Array.mapi ( fun ix x -> tryMapFileVariable ( $" %s {name}.%i {ix}" , x))
125- |> Array.collect ( Option.defaultValue [||])
126- |> Some
139+ x
140+ |> Array.mapi ( fun ix x -> tryMapFileVariable ( $" %s {name}.%i {ix}" , x))
141+ |> Array.collect ( Option.defaultValue [||])
142+ |> Some
127143 | _ -> None
128- request.Variables |> Array.collect ( tryMapFileVariable >> ( Option.defaultValue [||]))
144+ request.Variables
145+ |> Array.collect ( tryMapFileVariable >> ( Option.defaultValue [||]))
146+
129147 let operationContent =
130148 let variables =
131149 match request.Variables with
132- | null | [||] -> JsonValue.Null
133- | _ -> request.Variables |> Map.ofArray |> Serialization.toJsonValue
150+ | null
151+ | [||] -> JsonValue.Null
152+ | _ ->
153+ request.Variables
154+ |> Map.ofArray
155+ |> Serialization.toJsonValue
134156 let operationName =
135157 match request.OperationName with
136158 | Some x -> JsonValue.String x
137159 | None -> JsonValue.Null
138160 let json =
139- [| " operationName" , operationName
140- " query" , JsonValue.String request.Query
141- " variables" , variables |]
161+ [|
162+ " operationName" , operationName
163+ " query" , JsonValue.String request.Query
164+ " variables" , variables
165+ |]
142166 |> JsonValue.Record
143- let content = new StringContent( json.ToString( JsonSaveOptions.DisableFormatting))
144- content.Headers.Add( " Content-Disposition" , " form-data; name=\" operations\" " )
167+ let content = new StringContent ( json.ToString ( JsonSaveOptions.DisableFormatting))
168+ content.Headers.Add ( " Content-Disposition" , " form-data; name=\" operations\" " )
145169 content
146- content.Add( operationContent)
170+ content.Add ( operationContent)
147171 let mapContent =
148172 let files =
149173 files
150- |> Array.mapi ( fun ix ( name , _ ) -> ix.ToString(), JsonValue.Array [| JsonValue.String ( " variables." + name) |])
174+ |> Array.mapi ( fun ix ( name , _ ) -> ix.ToString (), JsonValue.Array [| JsonValue.String ( " variables." + name) |])
151175 |> JsonValue.Record
152- let content = new StringContent( files.ToString( JsonSaveOptions.DisableFormatting))
153- content.Headers.Add( " Content-Disposition" , " form-data; name=\" map\" " )
176+ let content = new StringContent ( files.ToString ( JsonSaveOptions.DisableFormatting))
177+ content.Headers.Add ( " Content-Disposition" , " form-data; name=\" map\" " )
154178 content
155- content.Add( mapContent)
179+ content.Add ( mapContent)
156180 let fileContents =
157181 files
158- |> Array .mapi ( fun ix ( _ , value ) ->
159- let content = new StreamContent( value.Stream)
160- content.Headers.Add( " Content-Disposition" , $" form-data; name=\" %i {ix }\" ; filename=\" %s {value.FileName}\" " )
161- content.Headers.Add( " Content-Type" , value.ContentType)
182+ |> Seq .mapi ( fun _ ( _ , value ) ->
183+ let content = new StreamContent ( value.Stream)
184+ content.Headers.Add ( " Content-Disposition" , $" form-data; name=\" %s {value.Name }\" ; filename=\" %s {value.FileName}\" " )
185+ content.Headers.Add ( " Content-Type" , value.ContentType)
162186 content)
163- fileContents |> Array .iter content.Add
187+ fileContents |> Seq .iter content.Add
164188 let! result = postAsync ct invoker request.ServerUrl request.HttpHeaders content
165189 return result
166190 }
167191
168192 /// Executes a multipart request to a GraphQL server.
169193 let sendMultipartRequest connection request =
170- ( sendMultipartRequestAsync CancellationToken.None connection request) .GetAwaiter() .GetResult()
194+ ( sendMultipartRequestAsync CancellationToken.None connection request) .GetAwaiter() .GetResult ()
0 commit comments