Skip to content

Commit cf00481

Browse files
committed
Ability to upload to Generic Packages Repository
1 parent f2d7b02 commit cf00481

19 files changed

+435
-2
lines changed

NGitLab.Mock/Clients/GitLabClient.cs

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public GitLabClient(ClientContext context)
1313

1414
public IGroupsClient Groups => new GroupClient(Context);
1515

16+
public IPackageClient Packages => new PackageClient(Context);
17+
1618
public IUserClient Users => new UserClient(Context);
1719

1820
public IProjectClient Projects => new ProjectClient(Context);

NGitLab.Mock/Clients/PackageClient.cs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Collections.Generic;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using NGitLab.Models;
5+
6+
namespace NGitLab.Mock.Clients
7+
{
8+
internal sealed class PackageClient : ClientBase, IPackageClient
9+
{
10+
public PackageClient(ClientContext context)
11+
: base(context)
12+
{
13+
}
14+
15+
public Task<Package> PublishAsync(int projectId, PackagePublish packagePublish, CancellationToken cancellationToken = default)
16+
{
17+
throw new System.NotImplementedException();
18+
}
19+
20+
public IEnumerable<PackageSearchResult> Get(int projectId, PackageQuery packageQuery)
21+
{
22+
throw new System.NotImplementedException();
23+
}
24+
25+
public Task<PackageSearchResult> GetByIdAsync(int projectId, int packageId, CancellationToken cancellationToken = default)
26+
{
27+
throw new System.NotImplementedException();
28+
}
29+
}
30+
}

NGitLab.Tests/PackageTests.cs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.IO;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using NGitLab.Models;
5+
using NGitLab.Tests.Docker;
6+
using NUnit.Framework;
7+
8+
namespace NGitLab.Tests
9+
{
10+
public class PackageTests
11+
{
12+
[Test]
13+
[NGitLabRetry]
14+
public async Task Test_publish_package()
15+
{
16+
using var context = await GitLabTestContext.CreateAsync();
17+
var project = context.CreateProject();
18+
var packagesClient = context.Client.Packages;
19+
20+
var packagePublish = new PackagePublish
21+
{
22+
FileName = "README.md",
23+
PackageName = "Packages",
24+
PackageVersion = "1.0.0",
25+
Status = "default",
26+
PackageStream = File.OpenRead("../../../../README.md"),
27+
};
28+
29+
var newGenericPackage = await packagesClient.PublishAsync(project.Id, packagePublish);
30+
31+
var packageQuery = new PackageQuery { PackageType = PackageType.generic };
32+
var genericPackages = packagesClient.Get(project.Id, packageQuery).ToList();
33+
var singleGenericPackage = await packagesClient.GetByIdAsync(project.Id, newGenericPackage.PackageId);
34+
35+
Assert.AreEqual(1, genericPackages.Count);
36+
Assert.AreEqual(newGenericPackage.PackageId, genericPackages[0].PackageId);
37+
Assert.AreEqual(singleGenericPackage.PackageId, newGenericPackage.PackageId);
38+
}
39+
}
40+
}

NGitLab/GitLabClient.cs

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ public class GitLabClient : IGitLabClient
88
{
99
private readonly API _api;
1010

11+
public IPackageClient Packages { get; }
12+
1113
public IUserClient Users { get; }
1214

1315
public IProjectClient Projects { get; }
@@ -73,6 +75,7 @@ public GitLabClient(string hostUrl, string userName, string password, RequestOpt
7375
private GitLabClient(GitLabCredentials credentials, RequestOptions options)
7476
{
7577
_api = new API(credentials, options);
78+
Packages = new PackageClient(_api);
7679
Users = new UserClient(_api);
7780
Projects = new ProjectClient(_api);
7881
MergeRequests = new MergeRequestClient(_api);

NGitLab/IGitLabClient.cs

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
{
33
public interface IGitLabClient
44
{
5+
IPackageClient Packages { get; }
6+
57
IUserClient Users { get; }
68

79
IProjectClient Projects { get; }

NGitLab/IPackageClient.cs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System.Collections.Generic;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using NGitLab.Models;
5+
6+
namespace NGitLab
7+
{
8+
public interface IPackageClient
9+
{
10+
/// <summary>
11+
/// Add a package file with the proposed information to the GitLab Generic Package Repository for the selected Project Id.
12+
/// </summary>
13+
/// <param name="packagePublish">The information about the package file to publish.</param>
14+
/// <returns>The package if it was created. Null if not.</returns>
15+
Task<Package> PublishAsync(int projectId, PackagePublish packagePublish, CancellationToken cancellationToken = default);
16+
17+
/// <summary>
18+
/// Gets all project packages based on the provided query parameters.
19+
/// </summary>
20+
/// <param name="projectId">The project id to search for packages in.</param>
21+
/// <param name="packageQuery">The query parameters to be used for the search.</param>
22+
/// <returns></returns>
23+
IEnumerable<PackageSearchResult> Get(int projectId, PackageQuery packageQuery);
24+
25+
/// <summary>
26+
/// Gets a single project package using the provided project and package ids.
27+
/// </summary>
28+
/// <param name="projectId">The project id that the package resides in.</param>
29+
/// <param name="packageId">The package id that is being selected.</param>
30+
/// <param name="cancellationToken">The cancellation token used to halt the request.</param>
31+
/// <returns></returns>
32+
Task<PackageSearchResult> GetByIdAsync(int projectId, int packageId, CancellationToken cancellationToken = default);
33+
}
34+
}

NGitLab/Impl/HttpRequestor.GitLabRequest.cs

+17-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ private sealed class GitLabRequest
2525

2626
public string JsonData { get; }
2727

28+
public FileContent FileContent { get; }
29+
2830
public FormDataContent FormData { get; }
2931

3032
private MethodType Method { get; }
@@ -60,7 +62,11 @@ public GitLabRequest(Uri url, MethodType method, object data, string apiToken, R
6062
Headers.Add("User-Agent", options.UserAgent);
6163
}
6264

63-
if (data is FormDataContent formData)
65+
if (data is FileContent fileContent)
66+
{
67+
FileContent = fileContent;
68+
}
69+
else if (data is FormDataContent formData)
6470
{
6571
FormData = formData;
6672
}
@@ -165,7 +171,11 @@ private HttpWebRequest CreateRequest(RequestOptions options)
165171

166172
if (HasOutput)
167173
{
168-
if (FormData != null)
174+
if (FileContent != null)
175+
{
176+
AddFileContent(request, options);
177+
}
178+
else if (FormData != null)
169179
{
170180
AddFileData(request, options);
171181
}
@@ -182,6 +192,11 @@ private HttpWebRequest CreateRequest(RequestOptions options)
182192
return request;
183193
}
184194

195+
private void AddFileContent(HttpWebRequest request, RequestOptions options)
196+
{
197+
FileContent.Stream.CopyTo(options.GetRequestStream(request));
198+
}
199+
185200
private void AddJsonData(HttpWebRequest request, RequestOptions options)
186201
{
187202
request.ContentType = "application/json";

NGitLab/Impl/PackageClient.cs

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using NGitLab.Models;
7+
8+
namespace NGitLab.Impl
9+
{
10+
public class PackageClient : IPackageClient
11+
{
12+
private const string PublishPackageUrl = "/projects/{0}/packages/generic/{1}/{2}/{3}?status={4}&select=package_file";
13+
private const string GetPackagesUrl = "/projects/{0}/packages";
14+
private const string GetPackageUrl = "/projects/{0}/packages/{1}";
15+
16+
private readonly API _api;
17+
18+
public PackageClient(API api)
19+
{
20+
_api = api;
21+
}
22+
23+
public Task<Package> PublishAsync(int projectId, PackagePublish packagePublish, CancellationToken cancellationToken = default)
24+
{
25+
var formData = new FileContent(packagePublish.PackageStream);
26+
27+
return _api.Put().With(formData).ToAsync<Package>(string.Format(CultureInfo.InvariantCulture,
28+
PublishPackageUrl, projectId, Uri.EscapeDataString(packagePublish.PackageName),
29+
Uri.EscapeDataString(packagePublish.PackageVersion), Uri.EscapeDataString(packagePublish.FileName),
30+
Uri.EscapeDataString(packagePublish.Status)), cancellationToken);
31+
}
32+
33+
public IEnumerable<PackageSearchResult> Get(int projectId, PackageQuery packageQuery)
34+
{
35+
var url = CreateGetUrl(projectId, packageQuery);
36+
return _api.Get().GetAllAsync<PackageSearchResult>(url);
37+
}
38+
39+
public Task<PackageSearchResult> GetByIdAsync(int projectId, int packageId, CancellationToken cancellationToken = default)
40+
{
41+
return _api.Get().ToAsync<PackageSearchResult>(string.Format(CultureInfo.InvariantCulture, GetPackageUrl, projectId, packageId), cancellationToken);
42+
}
43+
44+
private static string CreateGetUrl(int projectId, PackageQuery query)
45+
{
46+
var url = string.Format(CultureInfo.InvariantCulture, GetPackagesUrl, projectId);
47+
48+
url = Utils.AddParameter(url, "order_by", query.OrderBy);
49+
url = Utils.AddParameter(url, "sort", query.Sort);
50+
url = Utils.AddParameter(url, "status", query.Status);
51+
url = Utils.AddParameter(url, "page", query.Page);
52+
url = Utils.AddParameter(url, "per_page", query.PerPage);
53+
54+
if (query.PackageType != PackageType.all)
55+
{
56+
url = Utils.AddParameter(url, "package_type", query.PackageType);
57+
}
58+
59+
if (!string.IsNullOrWhiteSpace(query.PackageName))
60+
{
61+
url = Utils.AddParameter(url, "package_name", query.PackageName);
62+
}
63+
64+
if (query.IncludeVersionless)
65+
{
66+
url = Utils.AddParameter(url, "include_versionless", true);
67+
}
68+
69+
return url;
70+
}
71+
}
72+
}

NGitLab/Models/FileContent.cs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.IO;
2+
3+
namespace NGitLab.Models
4+
{
5+
public sealed class FileContent
6+
{
7+
public FileContent(Stream stream)
8+
{
9+
Stream = stream;
10+
}
11+
12+
/// <summary>
13+
/// The stream to be uploaded.
14+
/// </summary>
15+
public Stream Stream { get; }
16+
}
17+
}

NGitLab/Models/Package.cs

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Text.Json.Serialization;
3+
using NGitLab.Impl.Json;
4+
5+
namespace NGitLab.Models
6+
{
7+
public class Package
8+
{
9+
[JsonPropertyName("id")]
10+
public int Id { get; set; }
11+
12+
[JsonPropertyName("package_id")]
13+
public int PackageId { get; set; }
14+
15+
[JsonPropertyName("created_at")]
16+
[JsonConverter(typeof(DateOnlyConverter))]
17+
public DateTime CreatedAt { get; set; }
18+
19+
[JsonPropertyName("updated_at")]
20+
[JsonConverter(typeof(DateOnlyConverter))]
21+
public DateTime? UpdatedAt { get; set; }
22+
23+
[JsonPropertyName("size")]
24+
public int Size { get; set; }
25+
26+
[JsonPropertyName("file_store")]
27+
public int FileStore { get; set; }
28+
29+
[JsonPropertyName("file_md5")]
30+
public string FileMD5 { get; set; }
31+
32+
[JsonPropertyName("file_sha1")]
33+
public string FileSHA1 { get; set; }
34+
35+
[JsonPropertyName("file_sha256")]
36+
public string FileSHA256 { get; set; }
37+
38+
[JsonPropertyName("file_name")]
39+
public string FileName { get; set; }
40+
41+
[JsonPropertyName("verification_retry_at")]
42+
[JsonConverter(typeof(DateOnlyConverter))]
43+
public DateTime? VerificationRetryAt { get; set; }
44+
45+
[JsonPropertyName("verified_at")]
46+
[JsonConverter(typeof(DateOnlyConverter))]
47+
public DateTime? VerifiedAt { get; set; }
48+
49+
[JsonPropertyName("verification_failure")]
50+
public string VerificationFailure { get; set; }
51+
52+
[JsonPropertyName("verification_retry_count")]
53+
public string VerificationRetryCount { get; set; }
54+
55+
[JsonPropertyName("verification_checksum")]
56+
public string VerificationChecksum { get; set; }
57+
58+
[JsonPropertyName("verification_state")]
59+
public int VerificationState { get; set; }
60+
61+
[JsonPropertyName("verification_started_at")]
62+
[JsonConverter(typeof(DateOnlyConverter))]
63+
public DateTime? VerificationStartedAt { get; set; }
64+
65+
[JsonPropertyName("new_file_path")]
66+
public string NewFilePath { get; set; }
67+
68+
[JsonPropertyName("status")]
69+
public string Status { get; set; }
70+
71+
[JsonPropertyName("file")]
72+
public PackageFile File { get; set; }
73+
}
74+
}

NGitLab/Models/PackageFile.cs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace NGitLab.Models
4+
{
5+
public class PackageFile
6+
{
7+
[JsonPropertyName("url")]
8+
public string Url { get; set; }
9+
}
10+
}

NGitLab/Models/PackageLink.cs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace NGitLab.Models
4+
{
5+
public class PackageLinks
6+
{
7+
[JsonPropertyName("web_path")]
8+
public string WebPath { get; set; }
9+
10+
[JsonPropertyName("delete_api_path")]
11+
public string DeleteApiPath { get; set; }
12+
}
13+
}

NGitLab/Models/PackageOrderBy.cs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace NGitLab.Models
2+
{
3+
public enum PackageOrderBy
4+
{
5+
created_at,
6+
name,
7+
version,
8+
type,
9+
}
10+
}

0 commit comments

Comments
 (0)