Skip to content

Commit e6ac5b3

Browse files
authored
Implemented File Upload Client. (#6358)
1 parent d07fcee commit e6ac5b3

File tree

15 files changed

+872
-16
lines changed

15 files changed

+872
-16
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System;
2+
using System.IO;
3+
using static HotChocolate.Transport.Properties.TransportAbstractionResoucrces;
4+
5+
namespace HotChocolate.Transport.Http;
6+
7+
/// <summary>
8+
/// A file reference can be used to upload a file with the
9+
/// GraphQL multipart request protocol.
10+
/// </summary>
11+
public sealed class FileReference
12+
{
13+
private readonly Func<Stream> _openRead;
14+
15+
/// <summary>
16+
/// Creates a new instance of <see cref="FileReference" />
17+
/// </summary>
18+
/// <param name="stream">
19+
/// The file stream.
20+
/// </param>
21+
/// <param name="fileName">
22+
/// The file name.
23+
/// </param>
24+
/// <exception cref="ArgumentNullException">
25+
/// <paramref name="stream"/> is <c>null</c>.
26+
/// </exception>
27+
/// <exception cref="ArgumentException">
28+
/// <paramref name="fileName"/> is <c>null</c>, empty or white space.
29+
/// </exception>
30+
public FileReference(Stream stream, string fileName)
31+
{
32+
if (stream == null)
33+
{
34+
throw new ArgumentNullException(nameof(stream));
35+
}
36+
37+
if (string.IsNullOrWhiteSpace(fileName))
38+
{
39+
throw new ArgumentException(
40+
FileReference_FileName_NullOrEmpty,
41+
nameof(fileName));
42+
}
43+
44+
_openRead = () => stream;
45+
FileName = fileName;
46+
}
47+
48+
/// <summary>
49+
/// Creates a new instance of <see cref="FileReference" />
50+
/// </summary>
51+
/// <param name="openRead">
52+
/// The stream factory.
53+
/// </param>
54+
/// <param name="fileName">
55+
/// The file name.
56+
/// </param>
57+
/// <exception cref="ArgumentException">
58+
/// <paramref name="fileName"/> is <c>null</c>, empty or white space.
59+
/// </exception>
60+
/// <exception cref="ArgumentNullException">
61+
/// <paramref name="openRead"/> is <c>null</c>.
62+
/// </exception>
63+
public FileReference(Func<Stream> openRead, string fileName)
64+
{
65+
if (string.IsNullOrWhiteSpace(fileName))
66+
{
67+
throw new ArgumentException(
68+
FileReference_FileName_NullOrEmpty,
69+
nameof(fileName));
70+
}
71+
72+
_openRead = openRead ?? throw new ArgumentNullException(nameof(openRead));
73+
FileName = fileName;
74+
}
75+
76+
/// <summary>
77+
/// The file name eg. <c>foo.txt</c>.
78+
/// </summary>
79+
public string FileName { get; }
80+
81+
/// <summary>
82+
/// Opens the file stream.
83+
/// </summary>
84+
/// <returns>
85+
/// Returns the file stream.
86+
/// </returns>
87+
public Stream OpenRead() => _openRead();
88+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
namespace HotChocolate.Transport.Http;
2+
3+
/// <summary>
4+
/// The file reference info contains the actual
5+
/// <see cref="FileReference"/> and the reference
6+
/// name that is used to upload the referenced file.
7+
/// </summary>
8+
public sealed class FileReferenceInfo
9+
{
10+
/// <summary>
11+
/// Creates a new instance of <see cref="FileReferenceInfo" />
12+
/// </summary>
13+
/// <param name="file">
14+
/// The file reference.
15+
/// </param>
16+
/// <param name="name">
17+
/// The internal reference name.
18+
/// </param>
19+
internal FileReferenceInfo(FileReference file, string name)
20+
{
21+
File = file;
22+
Name = name;
23+
}
24+
25+
/// <summary>
26+
/// Gets the file that shall be uploaded.
27+
/// </summary>
28+
public FileReference File { get; }
29+
30+
/// <summary>
31+
/// Gets the internal reference name that is used to refer to
32+
/// this file instance within the GraphQL multipart request protocol.
33+
/// </summary>
34+
public string Name { get; }
35+
}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using HotChocolate.Language;
6+
using HotChocolate.Language.Utilities;
7+
8+
namespace HotChocolate.Transport.Http;
9+
10+
/// <summary>
11+
/// This file literal is used in order to allow for file references in <see cref="ObjectValueNode"/>.
12+
/// </summary>
13+
public sealed class FileReferenceNode
14+
: IValueNode<FileReference>
15+
, IValueNode<string>
16+
, IEquatable<FileReferenceNode>
17+
{
18+
/// <summary>
19+
/// Creates a new instance of <see cref="FileReferenceNode" />
20+
/// </summary>
21+
/// <param name="stream">
22+
/// The file stream.
23+
/// </param>
24+
/// <param name="fileName">
25+
/// The file name.
26+
/// </param>
27+
public FileReferenceNode(Stream stream, string fileName)
28+
: this(new FileReference(() => stream, fileName)) { }
29+
30+
/// <summary>
31+
/// Creates a new instance of <see cref="FileReferenceNode" />
32+
/// </summary>
33+
/// <param name="openRead">
34+
/// The stream factory.
35+
/// </param>
36+
/// <param name="fileName">
37+
/// The file name.
38+
/// </param>
39+
public FileReferenceNode(Func<Stream> openRead, string fileName)
40+
: this(new FileReference(openRead, fileName)) { }
41+
42+
/// <summary>
43+
/// Creates a new instance of <see cref="FileReferenceNode" />
44+
/// </summary>
45+
/// <param name="value">
46+
/// The file Reference.
47+
/// </param>
48+
public FileReferenceNode(FileReference value)
49+
{
50+
Value = value ?? throw new ArgumentNullException(nameof(value));
51+
}
52+
53+
/// <summary>
54+
/// Returns the <see cref="SyntaxKind"/> of the node.
55+
/// </summary>
56+
public SyntaxKind Kind => SyntaxKind.StringValue;
57+
58+
/// <summary>
59+
/// Gets a <see cref="Location"/> of this node in the parsed source text
60+
/// if available the parser provided this information.
61+
/// </summary>
62+
public Location? Location => null;
63+
64+
/// <summary>
65+
/// Gets the actual file reference.
66+
/// </summary>
67+
public FileReference Value { get; }
68+
69+
object? IValueNode.Value => Value;
70+
71+
string IValueNode<string>.Value => Value.FileName;
72+
73+
/// <summary>
74+
/// Gets the children of this node.
75+
/// </summary>
76+
/// <returns>
77+
/// Returns the children of this node..
78+
/// </returns>
79+
public IEnumerable<ISyntaxNode> GetNodes()
80+
=> Enumerable.Empty<ISyntaxNode>();
81+
82+
/// <summary>
83+
/// Determines whether the specified <see cref="IValueNode"/> is equal
84+
/// to the current <see cref="FileReferenceNode"/>.
85+
/// </summary>
86+
/// <param name="other">
87+
/// The <see cref="IValueNode"/> to compare with the current
88+
/// <see cref="FileReferenceNode"/>.
89+
/// </param>
90+
/// <returns>
91+
/// <c>true</c> if the specified <see cref="IValueNode"/> is equal
92+
/// to the current <see cref="FileReferenceNode"/>;
93+
/// otherwise, <c>false</c>.
94+
/// </returns>
95+
public bool Equals(FileReferenceNode? other)
96+
{
97+
if (ReferenceEquals(null, other))
98+
{
99+
return false;
100+
}
101+
102+
if (ReferenceEquals(this, other) ||
103+
ReferenceEquals(Value, other.Value))
104+
{
105+
return true;
106+
}
107+
108+
return false;
109+
}
110+
111+
/// <summary>
112+
/// Determines whether the specified <see cref="IValueNode"/> is equal
113+
/// to the current <see cref="FileReferenceNode"/>.
114+
/// </summary>
115+
/// <param name="other">
116+
/// The <see cref="IValueNode"/> to compare with the current
117+
/// <see cref="FileReferenceNode"/>.
118+
/// </param>
119+
/// <returns>
120+
/// <c>true</c> if the specified <see cref="IValueNode"/> is equal
121+
/// to the current <see cref="FileReferenceNode"/>;
122+
/// otherwise, <c>false</c>.
123+
/// </returns>
124+
public bool Equals(IValueNode? other)
125+
{
126+
if (ReferenceEquals(null, other))
127+
{
128+
return false;
129+
}
130+
131+
if (ReferenceEquals(this, other))
132+
{
133+
return true;
134+
}
135+
136+
if (other is FileReferenceNode file)
137+
{
138+
return Equals(file);
139+
}
140+
141+
return false;
142+
}
143+
144+
/// <summary>
145+
/// Determines whether the specified <see cref="object"/> is equal to
146+
/// the current <see cref="FileReferenceNode"/>.
147+
/// </summary>
148+
/// <param name="obj">
149+
/// The <see cref="object"/> to compare with the current
150+
/// <see cref="FileReferenceNode"/>.
151+
/// </param>
152+
/// <returns>
153+
/// <c>true</c> if the specified <see cref="object"/> is equal to the
154+
/// current <see cref="FileReferenceNode"/>; otherwise, <c>false</c>.
155+
/// </returns>
156+
public override bool Equals(object? obj)
157+
{
158+
if (ReferenceEquals(null, obj))
159+
{
160+
return false;
161+
}
162+
163+
if (ReferenceEquals(obj, this))
164+
{
165+
return true;
166+
}
167+
168+
return Equals(obj as FileReferenceNode);
169+
}
170+
171+
/// <summary>
172+
/// Serves as a hash function for a <see cref="FileReferenceNode"/>
173+
/// object.
174+
/// </summary>
175+
/// <returns>
176+
/// A hash code for this instance that is suitable for use in
177+
/// hashing algorithms and data structures such as a hash table.
178+
/// </returns>
179+
public override int GetHashCode()
180+
{
181+
unchecked
182+
{
183+
return (Kind.GetHashCode() * 397) ^ (Value.GetHashCode() * 97);
184+
}
185+
}
186+
187+
/// <summary>
188+
/// Returns the GraphQL syntax representation of this <see cref="ISyntaxNode"/>.
189+
/// </summary>
190+
/// <returns>
191+
/// Returns the GraphQL syntax representation of this <see cref="ISyntaxNode"/>.
192+
/// </returns>
193+
public override string ToString()
194+
=> ToString(true);
195+
196+
/// <summary>
197+
/// Returns the GraphQL syntax representation of this <see cref="ISyntaxNode"/>.
198+
/// </summary>
199+
/// <param name="indented">
200+
/// A value that indicates whether the GraphQL output should be formatted,
201+
/// which includes indenting nested GraphQL tokens, adding
202+
/// new lines, and adding white space between property names and values.
203+
/// </param>
204+
/// <returns>
205+
/// Returns the GraphQL syntax representation of this <see cref="ISyntaxNode"/>.
206+
/// </returns>
207+
public string ToString(bool indented)
208+
=> SyntaxPrinter.Print(this, indented);
209+
}

src/HotChocolate/AspNetCore/src/Transport.Abstractions/Properties/TransportAbstractionResoucrces.Designer.cs

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/HotChocolate/AspNetCore/src/Transport.Abstractions/Properties/TransportAbstractionResoucrces.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,7 @@
2424
<data name="OperationResult_Parse_JsonDataIsEmpty" xml:space="preserve">
2525
<value>The JSON document is empty.</value>
2626
</data>
27+
<data name="FileReference_FileName_NullOrEmpty" xml:space="preserve">
28+
<value>Value cannot be null or whitespace.</value>
29+
</data>
2730
</root>

0 commit comments

Comments
 (0)