Skip to content

Commit bc659f1

Browse files
yankunnmklotas
authored andcommitted
Upload files (#27)
* Added methods to upload a file to gitlab for later use in markdown aware fields. * Fixed typo caused by ReSharpers refactoring. * Form-part name ist now set as required. * Moved projectId property to Request model to ensure that pattern is the same verywhere.
1 parent 19456a7 commit bc659f1

File tree

9 files changed

+170
-1
lines changed

9 files changed

+170
-1
lines changed

src/GitLabApiClient/GitLabClient.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public GitLabClient(string hostUrl, string authenticationToken = "")
4444
var projectsGroupsQueryBuilder = new ProjectsGroupQueryBuilder();
4545

4646
Issues = new IssuesClient(_httpFacade, issuesQueryBuilder, projectIssuesQueryBuilder, projectIssueNotesQueryBuilder);
47+
Uploads = new UploadsClient(_httpFacade);
4748
MergeRequests = new MergeRequestsClient(_httpFacade, mergeRequestsQueryBuilder, projectMergeRequestsQueryBuilder);
4849
Projects = new ProjectsClient(_httpFacade, projectQueryBuilder, projectMilestonesQueryBuilder);
4950
Users = new UsersClient(_httpFacade);
@@ -55,6 +56,11 @@ public GitLabClient(string hostUrl, string authenticationToken = "")
5556
/// </summary>
5657
public IssuesClient Issues { get; }
5758

59+
/// <summary>
60+
/// Access GitLab's uploads API.
61+
/// </summary>
62+
public UploadsClient Uploads { get; }
63+
5864
/// <summary>
5965
/// Access GitLab's merge requests API.
6066
/// </summary>

src/GitLabApiClient/Internal/Http/GitLabHttpFacade.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System.Net.Http;
44
using System.Threading.Tasks;
55
using GitLabApiClient.Internal.Http.Serialization;
6+
using GitLabApiClient.Models.Uploads.Requests;
7+
using GitLabApiClient.Models.Uploads.Responses;
68
using GitLabApiClient.Models.Users.Responses;
79

810
namespace GitLabApiClient.Internal.Http
@@ -40,6 +42,9 @@ public Task<T> Post<T>(string uri, object data = null) where T : class =>
4042
public Task Post(string uri, object data = null) =>
4143
_requestor.Post(uri, data);
4244

45+
public Task<Upload> PostFile(string uri, CreateUploadRequest uploadRequest) =>
46+
_requestor.PostFile(uri, uploadRequest);
47+
4348
public Task<T> Put<T>(string uri, object data) =>
4449
_requestor.Put<T>(uri, data);
4550

src/GitLabApiClient/Internal/Http/GitlabApiRequestor.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
using System;
2+
using System.Globalization;
3+
using System.IO;
24
using System.Net.Http;
35
using System.Net.Http.Headers;
46
using System.Threading.Tasks;
57
using GitLabApiClient.Internal.Http.Serialization;
8+
using GitLabApiClient.Models.Uploads.Requests;
9+
using GitLabApiClient.Models.Uploads.Responses;
610

711
namespace GitLabApiClient.Internal.Http
812
{
@@ -39,6 +43,20 @@ public async Task Post(string url, object data = null)
3943
await EnsureSuccessStatusCode(responseMessage);
4044
}
4145

46+
public async Task<Upload> PostFile(string url, CreateUploadRequest uploadRequest)
47+
{
48+
using (var uploadContent =
49+
new MultipartFormDataContent($"Upload----{DateTime.Now.Ticks}"))
50+
{
51+
uploadContent.Add(new StreamContent(uploadRequest.Stream), "file", uploadRequest.FileName);
52+
53+
var responseMessage = await _client.PostAsync(url, uploadContent);
54+
await EnsureSuccessStatusCode(responseMessage);
55+
56+
return await ReadResponse<Upload>(responseMessage);
57+
}
58+
}
59+
4260
public async Task<T> Put<T>(string url, object data)
4361
{
4462
StringContent content = SerializeToString(data);
@@ -47,7 +65,7 @@ public async Task<T> Put<T>(string url, object data)
4765
return await ReadResponse<T>(responseMessage);
4866
}
4967

50-
public async Task Put(string url, object data)
68+
public async Task Put(string url, object data)
5169
{
5270
StringContent content = SerializeToString(data);
5371
var responseMessage = await _client.PutAsync(url, content);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Text;
5+
6+
namespace GitLabApiClient.Models.Uploads.Requests
7+
{
8+
/// <summary>
9+
/// A upload (file) for Gitlab, which can be embedded via markdown
10+
/// </summary>
11+
public sealed class CreateUploadRequest
12+
{
13+
public CreateUploadRequest(string projectId, Stream stream, string fileName)
14+
{
15+
projectId = projectId;
16+
Stream = stream;
17+
FileName = fileName;
18+
}
19+
20+
public string ProjectId { get; }
21+
22+
/// <summary>
23+
/// The stream to be uploaded
24+
/// </summary>
25+
public Stream Stream { get; }
26+
27+
/// <summary>
28+
/// The name of the file being uploaded
29+
/// </summary>
30+
public string FileName { get; }
31+
}
32+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Text;
5+
using Newtonsoft.Json;
6+
7+
namespace GitLabApiClient.Models.Uploads.Responses
8+
{
9+
public sealed class Upload
10+
{
11+
[JsonConstructor]
12+
public Upload(string alt, string url, string markdown)
13+
{
14+
Alt = alt;
15+
Url = url;
16+
Markdown = markdown;
17+
}
18+
19+
public string Alt { get; }
20+
public string Url { get; }
21+
public string Markdown { get; }
22+
}
23+
}

src/GitLabApiClient/UploadsClient.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
using GitLabApiClient.Internal.Http;
6+
using GitLabApiClient.Models.Uploads.Requests;
7+
using GitLabApiClient.Models.Uploads.Responses;
8+
9+
namespace GitLabApiClient
10+
{
11+
/// <summary>
12+
/// Uploads a file to a project. This file can later be used in issues or everywhere else, where you can write markdown.
13+
/// After uploading the file, use the <see cref="Upload.Markdown"/> property.
14+
/// </summary>
15+
public sealed class UploadsClient
16+
{
17+
private readonly GitLabHttpFacade _httpFacade;
18+
19+
internal UploadsClient(GitLabHttpFacade httpFacade) =>
20+
_httpFacade = httpFacade;
21+
22+
/// <summary>
23+
/// Uploadses a file for the provided project.
24+
/// </summary>
25+
/// <param name="projectId">The id of the project</param>
26+
/// <param name="uploadRequest">The upload request containing the filename and stream to be uploaded</param>
27+
/// <returns>A <see cref="Upload"/> object.
28+
/// Use the <see cref="Upload.Markdown"/> property to place the image in your markdown text.
29+
/// </returns>
30+
public async Task<Upload> UploadFile(CreateUploadRequest uploadRequest)
31+
{
32+
return await _httpFacade.PostFile($"projects/{uploadRequest.ProjectId}/uploads", uploadRequest);
33+
}
34+
}
35+
}

test/GitLabApiClient.Test/GitLabApiClient.Test.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
1212
<OutputPath>..\bin\Release\</OutputPath>
1313
</PropertyGroup>
14+
<ItemGroup>
15+
<None Remove="Ressources\gitlabtest.png" />
16+
</ItemGroup>
17+
<ItemGroup>
18+
<EmbeddedResource Include="Ressources\gitlabtest.png" />
19+
</ItemGroup>
1420
<ItemGroup>
1521
<PackageReference Include="FluentAssertions" Version="4.19.4" />
1622
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
Loading
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using FluentAssertions;
9+
using GitLabApiClient.Models.Uploads.Requests;
10+
using GitLabApiClient.Test.Utilities;
11+
using Xunit;
12+
using static GitLabApiClient.Test.Utilities.GitLabApiHelper;
13+
14+
namespace GitLabApiClient.Test
15+
{
16+
[Trait("Category", "LinuxIntegration")]
17+
[Collection("GitLabContainerFixture")]
18+
public class UploadsClientTest
19+
{
20+
private readonly UploadsClient _sut = new UploadsClient(
21+
GetFacade());
22+
23+
[Fact]
24+
public async Task UploadFile()
25+
{
26+
var assembly = Assembly.GetExecutingAssembly();
27+
var resourceName = assembly.GetManifestResourceNames()
28+
.Single(str => str.EndsWith("gitlabtest.png"));
29+
var fileName = $"{Guid.NewGuid()}";
30+
var fileNameWithExtension = $"{fileName}.png";
31+
32+
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
33+
{
34+
var upload = await _sut.UploadFile(
35+
new CreateUploadRequest(TestGroupTextId, stream, fileNameWithExtension));
36+
37+
upload.Alt.Should().Be(fileName, "Provided Alt tag should only contain the filename without the extension.");
38+
upload.Alt.Should().EndWith(fileName, "Provided Url should end with the filename provided to the upload method.");
39+
upload.Alt.Should().EndWith(fileName, "Provided Markdown should end with the filename provided to the upload method.");
40+
upload.Alt.Should().StartWith($"![{fileName}]", "Provided markdown should start with ![{fileName without extension}].");
41+
}
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)