Skip to content

Commit 3c7bb6b

Browse files
authored
Added FileData type instead of a Stream to provide a Content Type of a file (#532)
1 parent 9f43e95 commit 3c7bb6b

File tree

5 files changed

+41
-29
lines changed

5 files changed

+41
-29
lines changed

src/FSharp.Data.GraphQL.Server.AspNetCore/RequestExecutionContext.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type HttpContextRequestExecutionContext (httpContext : HttpContext) =
1313
else
1414
let form = httpContext.Request.Form
1515
match (form.Files |> Seq.vtryFind (fun f -> f.Name = key)) with
16-
| ValueSome file -> Ok (file.OpenReadStream())
16+
| ValueSome file ->
17+
Ok { Stream = file.OpenReadStream (); ContentType = file.ContentType }
1718
| ValueNone -> Error $"File with key '{key}' not found"
1819

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
namespace FSharp.Data.GraphQL
22

3+
type FileData = {
4+
Stream : System.IO.Stream
5+
ContentType : string
6+
}
7+
38
type IInputExecutionContext =
4-
abstract GetFile : string -> Result<System.IO.Stream, string>
9+
abstract GetFile : string -> Result<FileData, string>
510

611
type InputExecutionContextProvider = unit -> IInputExecutionContext
712

src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ module SchemaDefinitions =
191191
| _ -> Some(x.ToString())
192192

193193
/// Tries to convert any value to string.
194-
let coerceFileValue (context : IInputExecutionContext) (value : obj) : Result<System.IO.Stream, string> =
194+
let coerceFileValue (context : IInputExecutionContext) (value : obj) : Result<FileData, string> =
195195
match coerceStringValue value with
196196
| Some fileName -> context.GetFile fileName
197197
| None -> Error "Only string value can be used as file name"
@@ -482,31 +482,31 @@ module SchemaDefinitions =
482482
CoerceInput = coerceGuidInput
483483
CoerceOutput = coerceGuidValue }
484484

485-
/// Defines an object list filter for use as an argument for filter list of object fields.
486-
let FileType : InputCustomDefinition<System.IO.Stream> = {
485+
/// Defines an object list filter for use as an argument for filter list of object fields.
486+
let FileType : InputCustomDefinition<FileData> = {
487487
Name = "FileType"
488488
Description =
489489
Some
490490
"The `File` type represents a file on one or more fields of an object in an object list. The filter is represented by a JSON object where the fields are the complemented by specific suffixes to represent a query."
491491
CoerceInput =
492492
(fun inputContext input variables ->
493-
let getFileStream fileKey =
493+
let getFileData fileKey =
494494
let inputExecutionContext = inputContext()
495-
let streamResult = inputExecutionContext.GetFile fileKey
496-
match streamResult with
497-
| Ok stream -> Ok stream
495+
let fileData = inputExecutionContext.GetFile fileKey
496+
match fileData with
497+
| Ok data -> Ok data
498498
| Error errorMessage -> IGQLError.createResultErrorList errorMessage
499499

500500
match input with
501501
| InlineConstant c ->
502502
match c with
503-
| StringValue strValue -> getFileStream strValue
503+
| StringValue strValue -> getFileData strValue
504504
| VariableName varName ->
505-
Ok (variables[varName] :?> System.IO.Stream)
505+
Ok (variables[varName] :?> FileData)
506506
| _ -> IGQLError.createResultErrorList "Only a string value or a variable with a string value can be used as a file name."
507507
| Variable json ->
508508
match (json |> InputValue.OfJsonElement) with
509-
| StringValue str -> getFileStream str
509+
| StringValue str -> getFileData str
510510
| _ -> IGQLError.createResultErrorList "Only a variable with a string value can be used as a file name.")
511511
}
512512

tests/FSharp.Data.GraphQL.Tests/FileTests.fs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,15 @@ let QueryType =
1919
)
2020

2121
type Input = {
22-
File : Stream
23-
File2 : Stream option
22+
File : FileData
23+
File2 : FileData option
2424
}
2525

26+
let private getFullInfo fileData =
27+
use reader = new StreamReader(fileData.Stream, Encoding.UTF8, true)
28+
let fileContent = reader.ReadToEnd()
29+
fileContent + fileData.ContentType
30+
2631
let InputObject = Define.InputObject<Input>(
2732
name = "Input",
2833
fields =
@@ -37,21 +42,18 @@ let MutationType =
3742
fields =
3843
[ Define.Field ("uploadFile", StringType, "", [ Define.Input ("input", FileType) ],
3944
(fun ctx () ->
40-
let stream = ctx.Arg<Stream> "input"
41-
use reader = new StreamReader(stream, Encoding.UTF8, true)
42-
reader.ReadToEnd()
45+
let fileData = ctx.Arg<FileData> "input"
46+
getFullInfo fileData
4347
));
4448
Define.Field ("uploadFileComplex", StringType, "", [ Define.Input ("input", InputObject) ],
4549
(fun ctx () ->
4650
let input = ctx.Arg<Input> "input"
47-
let reader = new StreamReader(input.File, Encoding.UTF8, true)
48-
let fileContent = reader.ReadToEnd()
49-
let file2Content = match input.File2 with
51+
let fileString = getFullInfo input.File
52+
let file2String = match input.File2 with
5053
| Some file2 ->
51-
let reader2 = new StreamReader(file2, Encoding.UTF8, true)
52-
reader2.ReadToEnd()
54+
getFullInfo file2
5355
| None -> ""
54-
fileContent + file2Content
56+
fileString + file2String
5557
))
5658
]
5759
)
@@ -80,7 +82,7 @@ let mutationComplexObjectWithTwoFiles = """mutation uploadFile () {
8082

8183
[<Fact>]
8284
let ``File type: Must upload file as input scalar using inline string as a file name`` () =
83-
let expected = NameValueLookup.ofList [ "uploadFile", MockInputContext.mockFileText ]
85+
let expected = NameValueLookup.ofList [ "uploadFile", MockInputContext.mockFileTextAndContentType ]
8486
let result = execute mutationWithConstant
8587
ensureDirect result <| fun data errors ->
8688
empty errors
@@ -89,7 +91,7 @@ let ``File type: Must upload file as input scalar using inline string as a file
8991

9092
[<Fact>]
9193
let ``File type: Must upload a file as input scalar using a variable`` () =
92-
let expected = NameValueLookup.ofList [ "uploadFile", MockInputContext.mockFileText ]
94+
let expected = NameValueLookup.ofList [ "uploadFile", MockInputContext.mockFileTextAndContentType ]
9395
let jsonVariable = "\"fileKey\"" |> JsonDocument.Parse |> _.RootElement
9496
let variables = ImmutableDictionary<string, JsonElement>.Empty.Add ("file", jsonVariable)
9597
let result = executeWithVariables (mutationWithVariable, variables)
@@ -100,7 +102,7 @@ let ``File type: Must upload a file as input scalar using a variable`` () =
100102

101103
[<Fact>]
102104
let ``File type: Must upload a file as input object field using inline string a file name`` () =
103-
let expected = NameValueLookup.ofList [ "uploadFileComplex", MockInputContext.mockFileText ]
105+
let expected = NameValueLookup.ofList [ "uploadFileComplex", MockInputContext.mockFileTextAndContentType ]
104106
let result = execute mutationComplexObject
105107
ensureDirect result <| fun data errors ->
106108
empty errors
@@ -109,7 +111,7 @@ let ``File type: Must upload a file as input object field using inline string a
109111

110112
[<Fact>]
111113
let ``File type: Must upload two files as input object field using inline string a file name`` () =
112-
let expectedContent = MockInputContext.mockFileText + MockInputContext.mockFileText2
114+
let expectedContent = MockInputContext.mockFileTextAndContentType + MockInputContext.mockFileText2AndContentType
113115
let expected = NameValueLookup.ofList [
114116
"uploadFileComplex", expectedContent
115117
]

tests/FSharp.Data.GraphQL.Tests/Helpers.fs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,15 @@ type ExecutorExtensions =
185185

186186
module MockInputContext =
187187

188+
let mockContentType = System.Net.Mime.MediaTypeNames.Text.Plain
188189
let mockFileKey = "fileKey"
189190
let mockFileKey2 = "fileKey2"
190191
let mockFileText = "fileText"
191192
let mockFileText2 = "fileText2"
192193

194+
let mockFileTextAndContentType = mockFileText + mockContentType
195+
let mockFileText2AndContentType = mockFileText2 + mockContentType
196+
193197
type MockInputExecutionContext () =
194198

195199
member _.FileKey = mockFileKey
@@ -207,9 +211,9 @@ module MockInputContext =
207211
interface IInputExecutionContext with
208212
member context.GetFile key =
209213
if (key = context.FileKey) then
210-
Ok context.Stream
214+
Ok { Stream = context.Stream; ContentType = mockContentType }
211215
else if (key = context.FileKey2) then
212-
Ok context.Stream2
216+
Ok { Stream = context.Stream2; ContentType = mockContentType }
213217
else
214218
failwith $"only file {context.FileKey} and file {context.FileKey2} exist"
215219

0 commit comments

Comments
 (0)