Skip to content

Commit a95f947

Browse files
Merge pull request #488 from notion-dotnet/478-add-support-for-send-file-upload-endpoint
Add support for send file upload API
2 parents aaecefe + fd6c4fb commit a95f947

16 files changed

+433
-99
lines changed

Src/Notion.Client/Api/ApiEndpoints.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ public static class AuthenticationUrls
143143
public static class FileUploadsApiUrls
144144
{
145145
public static string Create() => "/v1/file_uploads";
146+
public static string Send(string fileUploadId) => $"/v1/file_uploads/{fileUploadId}/send";
146147
}
147148
}
148149
}

Src/Notion.Client/Api/FileUploads/IFileUploadsClient.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,19 @@ Task<CreateFileUploadResponse> CreateAsync(
1515
CreateFileUploadRequest fileUploadObjectRequest,
1616
CancellationToken cancellationToken = default
1717
);
18+
19+
/// <summary>
20+
/// Send a file upload
21+
///
22+
/// Requires a `file_upload_id`, obtained from the `id` of the Create File Upload API response.
23+
///
24+
/// </summary>
25+
/// <param name="sendFileUploadRequest"></param>
26+
/// <param name="cancellationToken"></param>
27+
/// <returns></returns>
28+
Task<SendFileUploadResponse> SendAsync(
29+
SendFileUploadRequest sendFileUploadRequest,
30+
CancellationToken cancellationToken = default
31+
);
1832
}
1933
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
5+
namespace Notion.Client
6+
{
7+
public sealed partial class FileUploadsClient
8+
{
9+
public async Task<SendFileUploadResponse> SendAsync(
10+
SendFileUploadRequest sendFileUploadRequest,
11+
CancellationToken cancellationToken = default)
12+
{
13+
if (sendFileUploadRequest == null)
14+
{
15+
throw new ArgumentNullException(nameof(sendFileUploadRequest));
16+
}
17+
18+
if (string.IsNullOrWhiteSpace(sendFileUploadRequest.FileUploadId))
19+
{
20+
throw new ArgumentNullException(nameof(sendFileUploadRequest.FileUploadId));
21+
}
22+
23+
if (sendFileUploadRequest.PartNumber != null)
24+
{
25+
if (!int.TryParse(sendFileUploadRequest.PartNumber, out int partNumberValue) || partNumberValue < 1 || partNumberValue > 1000)
26+
{
27+
throw new ArgumentOutOfRangeException(nameof(sendFileUploadRequest.PartNumber), "PartNumber must be between 1 and 1000.");
28+
}
29+
}
30+
31+
var path = ApiEndpoints.FileUploadsApiUrls.Send(sendFileUploadRequest.FileUploadId);
32+
33+
return await _restClient.PostAsync<SendFileUploadResponse>(
34+
path,
35+
formData: sendFileUploadRequest,
36+
cancellationToken: cancellationToken
37+
);
38+
}
39+
}
40+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.IO;
2+
3+
namespace Notion.Client
4+
{
5+
public class FileData
6+
{
7+
/// <summary>
8+
/// The name of the file being uploaded.
9+
/// </summary>
10+
public string FileName { get; set; }
11+
12+
/// <summary>
13+
/// The content of the file being uploaded.
14+
/// </summary>
15+
public Stream Data { get; set; }
16+
17+
/// <summary>
18+
/// The MIME type of the file being uploaded.
19+
/// </summary>
20+
public string ContentType { get; set; }
21+
}
22+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Notion.Client
4+
{
5+
public interface ISendFileUploadFormDataParameters
6+
{
7+
/// <summary>
8+
/// The raw binary file contents to upload.
9+
/// </summary>
10+
FileData File { get; }
11+
12+
/// <summary>
13+
/// When using a mode=multi_part File Upload to send files greater than 20 MB in parts, this is the current part number.
14+
/// Must be an integer between 1 and 1000 provided as a string form field.
15+
/// </summary>
16+
string PartNumber { get; }
17+
}
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Notion.Client
2+
{
3+
public interface ISendFileUploadPathParameters
4+
{
5+
/// <summary>
6+
/// The `file_upload_id` obtained from the `id` of the Create File Upload API response.
7+
/// </summary>
8+
string FileUploadId { get; }
9+
}
10+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace Notion.Client
2+
{
3+
public class SendFileUploadRequest : ISendFileUploadFormDataParameters, ISendFileUploadPathParameters
4+
{
5+
public FileData File { get; private set; }
6+
public string PartNumber { get; private set; }
7+
public string FileUploadId { get; private set; }
8+
9+
private SendFileUploadRequest() { }
10+
11+
public static SendFileUploadRequest Create(string fileUploadId, FileData file, string partNumber = null)
12+
{
13+
return new SendFileUploadRequest
14+
{
15+
FileUploadId = fileUploadId,
16+
File = file,
17+
PartNumber = partNumber
18+
};
19+
}
20+
}
21+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Notion.Client
2+
{
3+
public class SendFileUploadResponse : FileObjectResponse
4+
{
5+
}
6+
}

Src/Notion.Client/RestClient/IRestClient.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ Task<T> PostAsync<T>(
2323
IBasicAuthenticationParameters basicAuthenticationParameters = null,
2424
CancellationToken cancellationToken = default);
2525

26+
Task<T> PostAsync<T>(
27+
string uri,
28+
ISendFileUploadFormDataParameters formData,
29+
IEnumerable<KeyValuePair<string, string>> queryParams = null,
30+
IDictionary<string, string> headers = null,
31+
JsonSerializerSettings serializerSettings = null,
32+
IBasicAuthenticationParameters basicAuthenticationParameters = null,
33+
CancellationToken cancellationToken = default);
34+
2635
Task<T> PatchAsync<T>(
2736
string uri,
2837
object body,

Src/Notion.Client/RestClient/RestClient.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,46 @@ void AttachContent(HttpRequestMessage httpRequest)
7070
return await response.ParseStreamAsync<T>(serializerSettings);
7171
}
7272

73+
public async Task<T> PostAsync<T>(
74+
string uri,
75+
ISendFileUploadFormDataParameters formData,
76+
IEnumerable<KeyValuePair<string, string>> queryParams = null,
77+
IDictionary<string, string> headers = null,
78+
JsonSerializerSettings serializerSettings = null,
79+
IBasicAuthenticationParameters basicAuthenticationParameters = null,
80+
CancellationToken cancellationToken = default)
81+
{
82+
void AttachContent(HttpRequestMessage httpRequest)
83+
{
84+
var fileContent = new StreamContent(formData.File.Data);
85+
fileContent.Headers.ContentType = new MediaTypeHeaderValue(formData.File.ContentType);
86+
87+
var form = new MultipartFormDataContent
88+
{
89+
{ fileContent, "file", formData.File.FileName }
90+
};
91+
92+
if (!string.IsNullOrEmpty(formData.PartNumber))
93+
{
94+
form.Add(new StringContent(formData.PartNumber), "part_number");
95+
}
96+
97+
httpRequest.Content = form;
98+
}
99+
100+
var response = await SendAsync(
101+
uri,
102+
HttpMethod.Post,
103+
queryParams,
104+
headers,
105+
AttachContent,
106+
basicAuthenticationParameters,
107+
cancellationToken
108+
);
109+
110+
return await response.ParseStreamAsync<T>(serializerSettings);
111+
}
112+
73113
public async Task<T> PatchAsync<T>(
74114
string uri,
75115
object body,

0 commit comments

Comments
 (0)