Skip to content

Commit

Permalink
Changes to prevent breaking change to IRepositoryClient.GetArchive
Browse files Browse the repository at this point in the history
Adds a new overload to IRepository.GetArchive which accepts a query object in order to allow additional parameters to be passed to the the archive endpoint without breaking the existing implementation.
  • Loading branch information
steve85 committed Apr 12, 2024
1 parent b8d48da commit 93889b4
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 32 deletions.
7 changes: 6 additions & 1 deletion NGitLab.Mock/Clients/RepositoryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,12 @@ public void GetRawBlob(string sha, Action<Stream> parser)
throw new NotImplementedException();
}

public void GetArchive(Action<Stream> parser, string sha = null, string format = null)
public void GetArchive(Action<Stream> parser)
{
throw new NotImplementedException();
}

public void GetArchive(Action<Stream> parser, FileArchiveQuery fileArchiveQuery)
{
throw new NotImplementedException();
}
Expand Down
96 changes: 76 additions & 20 deletions NGitLab.Tests/RepositoryClient/RepositoryClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,10 @@ public async Task GetCommitRefs(CommitRefType type)

[Test]
[NGitLabRetry]
public async Task GetArchiveWithoutOptionalParameters()
public async Task GetArchive()
{
// Arrange
using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 2);
using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 2).ConfigureAwait(false);

// Act
context.RepositoryClient.GetArchive((stream) => { });
Expand All @@ -376,78 +376,134 @@ public async Task GetArchiveWithoutOptionalParameters()

[Test]
[NGitLabRetry]
public async Task GetArchiveAcceptsShaParameter()
public async Task GetArchiveWithNullQueryPassesNoParameters()
{
// Arrange
using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 2);
using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 2).ConfigureAwait(false);
var firstCommitId = context.Commits[0].Id.ToString();

// Act
context.RepositoryClient.GetArchive((stream) => { }, sha: firstCommitId);
context.RepositoryClient.GetArchive((stream) => { }, fileArchiveQuery: null);

// Assert
var requestPathAndQuery = context.Context.LastRequest.RequestUri.PathAndQuery;

Assert.Multiple(() =>
{
Assert.That(requestPathAndQuery, Is.Not.Null);
Assert.That(requestPathAndQuery.Contains($"?sha={firstCommitId}", StringComparison.OrdinalIgnoreCase), Is.True);
Assert.That(requestPathAndQuery.EndsWith("/archive", StringComparison.OrdinalIgnoreCase), Is.True);
});
}

[Test]
[TestCase(null, "")]
[TestCase(FileArchiveFormat.Bz2, ".bz2")]
[TestCase(FileArchiveFormat.Gz, ".gz")]
[TestCase(FileArchiveFormat.Tar, ".tar")]
[TestCase(FileArchiveFormat.TarBz2, ".tar.bz2")]
[TestCase(FileArchiveFormat.TarGz, ".tar.gz")]
[TestCase(FileArchiveFormat.Tb2, ".tb2")]
[TestCase(FileArchiveFormat.Tbz2, ".tbz2")]
[TestCase(FileArchiveFormat.Zip, ".zip")]
[NGitLabRetry]
public async Task GetArchiveAcceptsFormatParameter()
public async Task GetArchiveFormatValuePassedCorrectly(FileArchiveFormat? archiveFormat, string expectedExtension)
{
// Arrange
using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 2);
var format = ".zip";
var fileArchiveQuery = new FileArchiveQuery
{
Format = archiveFormat,
};

// Act
context.RepositoryClient.GetArchive((stream) => { }, format: format);
context.RepositoryClient.GetArchive((stream) => { }, fileArchiveQuery);

// Assert
var requestPathAndQuery = context.Context.LastRequest.RequestUri.PathAndQuery;

Assert.Multiple(() =>
{
Assert.That(requestPathAndQuery, Is.Not.Null);
Assert.That(requestPathAndQuery.Contains($"/archive{format}", StringComparison.OrdinalIgnoreCase), Is.True);
Assert.That(requestPathAndQuery.EndsWith($"/archive{expectedExtension}", StringComparison.OrdinalIgnoreCase), Is.True);
});
}

[Test]
[NGitLabRetry]
public async Task GetArchiveAcceptsShaAndFormatParametersTogether()
public async Task GetArchiveShaValuePassedCorrectly()
{
// Arrange
using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 2);
var format = ".zip";
var firstCommitId = context.Commits[0].Id.ToString();
var fileArchiveQuery = new FileArchiveQuery
{
Ref = firstCommitId,
};

// Act
context.RepositoryClient.GetArchive((stream) => { }, sha: firstCommitId, format: format);
context.RepositoryClient.GetArchive((stream) => { }, fileArchiveQuery);

// Assert
var requestPathAndQuery = context.Context.LastRequest.RequestUri.PathAndQuery;

Assert.Multiple(() =>
{
Assert.That(requestPathAndQuery, Is.Not.Null);
Assert.That(requestPathAndQuery.Contains($"/archive{format}", StringComparison.OrdinalIgnoreCase), Is.True);
Assert.That(requestPathAndQuery.Contains($"?sha={firstCommitId}", StringComparison.OrdinalIgnoreCase), Is.True);
Assert.That(requestPathAndQuery.Contains($"sha={firstCommitId}", StringComparison.OrdinalIgnoreCase), Is.True);
});
}

[Test]
[NGitLabRetry]
public async Task GetArchiveThrowsExceptionWhenFormatDoesNotStartWithDot()
public async Task GetArchivePathValuePassedCorrectly()
{
// Arrange
using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 2);
var format = "zip";
var path = RepositoryClientTestsContext.SubfolderName;
var fileArchiveQuery = new FileArchiveQuery
{
Path = path,
};

// Act and Assert
Assert.Throws<ArgumentException>(() => context.RepositoryClient.GetArchive((stream) => { }, format: format));
// Act
context.RepositoryClient.GetArchive((stream) => { }, fileArchiveQuery);

// Assert
var requestPathAndQuery = context.Context.LastRequest.RequestUri.PathAndQuery;

Assert.Multiple(() =>
{
Assert.That(requestPathAndQuery, Is.Not.Null);
Assert.That(requestPathAndQuery.Contains($"path={path}", StringComparison.OrdinalIgnoreCase), Is.True);
});
}

[Test]
[NGitLabRetry]
public async Task GetArchiveCombinationOfValuesPassedCorrectly()
{
// Arrange
using var context = await RepositoryClientTestsContext.CreateAsync(commitCount: 2);
var firstCommitId = context.Commits[0].Id.ToString();
var path = RepositoryClientTestsContext.SubfolderName;
var fileArchiveQuery = new FileArchiveQuery
{
Format = FileArchiveFormat.Zip,
Path = path,
Ref = firstCommitId,
};

// Act
context.RepositoryClient.GetArchive((stream) => { }, fileArchiveQuery);

// Assert
var requestPathAndQuery = context.Context.LastRequest.RequestUri.PathAndQuery;

Assert.Multiple(() =>
{
Assert.That(requestPathAndQuery, Is.Not.Null);
Assert.That(requestPathAndQuery.Contains($"/archive.zip", StringComparison.OrdinalIgnoreCase), Is.True);
Assert.That(requestPathAndQuery.Contains($"path={path}", StringComparison.OrdinalIgnoreCase), Is.True);
Assert.That(requestPathAndQuery.Contains($"sha={firstCommitId}", StringComparison.OrdinalIgnoreCase), Is.True);
});
}
}
20 changes: 20 additions & 0 deletions NGitLab/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;

namespace NGitLab.Extensions;

internal static class TypeExtensions
{
public static string GetEnumMemberAttributeValue<TEnum>(this TEnum value)
where TEnum : Enum
{
return typeof(TEnum)
.GetTypeInfo()
.DeclaredMembers
.SingleOrDefault(x => string.Equals(x.Name, value.ToString(), StringComparison.Ordinal))
?.GetCustomAttribute<EnumMemberAttribute>(inherit: false)
?.Value;
}
}
4 changes: 3 additions & 1 deletion NGitLab/IRepositoryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ public interface IRepositoryClient

void GetRawBlob(string sha, Action<Stream> parser);

void GetArchive(Action<Stream> parser, string sha = null, string format = null);
void GetArchive(Action<Stream> parser);

void GetArchive(Action<Stream> parser, FileArchiveQuery fileArchiveQuery);

IEnumerable<Commit> Commits { get; }

Expand Down
26 changes: 18 additions & 8 deletions NGitLab/Impl/RepositoryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,27 @@ public void GetRawBlob(string sha, Action<Stream> parser)
_api.Get().Stream(_repoPath + "/raw_blobs/" + sha, parser);
}

public void GetArchive(Action<Stream> parser, string sha = null, string format = null)
{
if (!string.IsNullOrEmpty(format) && !format.StartsWith(".", StringComparison.Ordinal))
throw new ArgumentException($"Format must include the '.' as part of extension", nameof(format));
public void GetArchive(Action<Stream> parser) => GetArchive(parser, fileArchiveQuery: null);

var relativePath = $"/archive{format}";
public void GetArchive(Action<Stream> parser, FileArchiveQuery fileArchiveQuery)
{
var url = $"{_repoPath}/archive";

if (!string.IsNullOrEmpty(sha))
relativePath += $"?sha={sha}";
if (fileArchiveQuery != null)
{
// If a particular archive file format is requested, it is appended to the path directly as follows:
// /project/123/repository/archive.zip
// /project/123/repository/archive.tar
if (fileArchiveQuery.Format.HasValue)
{
url += fileArchiveQuery.Format.Value.GetEnumMemberAttributeValue();
}

url = Utils.AddParameter(url, "path", fileArchiveQuery.Path);
url = Utils.AddParameter(url, "sha", fileArchiveQuery.Ref);
}

_api.Get().Stream(_repoPath + relativePath, parser);
_api.Get().Stream(url, parser);
}

public IEnumerable<Commit> Commits => _api.Get().GetAll<Commit>(_repoPath + $"/commits?per_page={GetCommitsRequest.DefaultPerPage}");
Expand Down
33 changes: 33 additions & 0 deletions NGitLab/Models/FileArchiveFormat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Runtime.Serialization;

namespace NGitLab.Models;

public enum FileArchiveFormat
{
[EnumMember(Value = ".bz2")]
Bz2,

[EnumMember(Value = ".gz")]
Gz,

[EnumMember(Value = ".tar")]
Tar,

[EnumMember(Value = ".tar.bz2")]
TarBz2,

[EnumMember(Value = ".tar.gz")]
TarGz,

[EnumMember(Value = ".tb2")]
Tb2,

[EnumMember(Value = ".tbz")]
Tbz,

[EnumMember(Value = ".tbz2")]
Tbz2,

[EnumMember(Value = ".zip")]
Zip,
}
12 changes: 12 additions & 0 deletions NGitLab/Models/FileArchiveQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace NGitLab.Models;

public sealed class FileArchiveQuery
{
public FileArchiveFormat? Format { get; set; }

// This property is named Ref because even though the query string parameter key is 'sha' it accepts any ref
// i.e. branch name, sha, tag
public string Ref { get; set; }

public string Path { get; set; }
}
24 changes: 22 additions & 2 deletions NGitLab/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,8 @@ NGitLab.Impl.RepositoryClient.Commits.get -> System.Collections.Generic.IEnumera
NGitLab.Impl.RepositoryClient.Compare(NGitLab.Models.CompareQuery query) -> NGitLab.Models.CompareResults
NGitLab.Impl.RepositoryClient.Contributors.get -> NGitLab.IContributorClient
NGitLab.Impl.RepositoryClient.Files.get -> NGitLab.IFilesClient
NGitLab.Impl.RepositoryClient.GetArchive(System.Action<System.IO.Stream> parser, string sha = null, string format = null) -> void
NGitLab.Impl.RepositoryClient.GetArchive(System.Action<System.IO.Stream> parser) -> void
NGitLab.Impl.RepositoryClient.GetArchive(System.Action<System.IO.Stream> parser, NGitLab.Models.FileArchiveQuery fileArchiveQuery) -> void
NGitLab.Impl.RepositoryClient.GetCommit(NGitLab.Sha1 sha) -> NGitLab.Models.Commit
NGitLab.Impl.RepositoryClient.GetCommitDiff(NGitLab.Sha1 sha) -> System.Collections.Generic.IEnumerable<NGitLab.Models.Diff>
NGitLab.Impl.RepositoryClient.GetCommitRefs(NGitLab.Sha1 sha, NGitLab.Models.CommitRefType type = NGitLab.Models.CommitRefType.All) -> System.Collections.Generic.IEnumerable<NGitLab.Models.Ref>
Expand Down Expand Up @@ -1086,7 +1087,8 @@ NGitLab.IRepositoryClient.Commits.get -> System.Collections.Generic.IEnumerable<
NGitLab.IRepositoryClient.Compare(NGitLab.Models.CompareQuery query) -> NGitLab.Models.CompareResults
NGitLab.IRepositoryClient.Contributors.get -> NGitLab.IContributorClient
NGitLab.IRepositoryClient.Files.get -> NGitLab.IFilesClient
NGitLab.IRepositoryClient.GetArchive(System.Action<System.IO.Stream> parser, string sha = null, string format = null) -> void
NGitLab.IRepositoryClient.GetArchive(System.Action<System.IO.Stream> parser) -> void
NGitLab.IRepositoryClient.GetArchive(System.Action<System.IO.Stream> parser, NGitLab.Models.FileArchiveQuery fileArchiveQuery) -> void
NGitLab.IRepositoryClient.GetCommit(NGitLab.Sha1 sha) -> NGitLab.Models.Commit
NGitLab.IRepositoryClient.GetCommitDiff(NGitLab.Sha1 sha) -> System.Collections.Generic.IEnumerable<NGitLab.Models.Diff>
NGitLab.IRepositoryClient.GetCommitRefs(NGitLab.Sha1 sha, NGitLab.Models.CommitRefType type = NGitLab.Models.CommitRefType.All) -> System.Collections.Generic.IEnumerable<NGitLab.Models.Ref>
Expand Down Expand Up @@ -1768,6 +1770,24 @@ NGitLab.Models.EventTargetType.Note = 4 -> NGitLab.Models.EventTargetType
NGitLab.Models.EventTargetType.Project = 5 -> NGitLab.Models.EventTargetType
NGitLab.Models.EventTargetType.Snippet = 6 -> NGitLab.Models.EventTargetType
NGitLab.Models.EventTargetType.User = 7 -> NGitLab.Models.EventTargetType
NGitLab.Models.FileArchiveFormat
NGitLab.Models.FileArchiveFormat.Bz2 = 0 -> NGitLab.Models.FileArchiveFormat
NGitLab.Models.FileArchiveFormat.Gz = 1 -> NGitLab.Models.FileArchiveFormat
NGitLab.Models.FileArchiveFormat.Tar = 2 -> NGitLab.Models.FileArchiveFormat
NGitLab.Models.FileArchiveFormat.TarBz2 = 3 -> NGitLab.Models.FileArchiveFormat
NGitLab.Models.FileArchiveFormat.TarGz = 4 -> NGitLab.Models.FileArchiveFormat
NGitLab.Models.FileArchiveFormat.Tb2 = 5 -> NGitLab.Models.FileArchiveFormat
NGitLab.Models.FileArchiveFormat.Tbz = 6 -> NGitLab.Models.FileArchiveFormat
NGitLab.Models.FileArchiveFormat.Tbz2 = 7 -> NGitLab.Models.FileArchiveFormat
NGitLab.Models.FileArchiveFormat.Zip = 8 -> NGitLab.Models.FileArchiveFormat
NGitLab.Models.FileArchiveQuery
NGitLab.Models.FileArchiveQuery.FileArchiveQuery() -> void
NGitLab.Models.FileArchiveQuery.Format.get -> NGitLab.Models.FileArchiveFormat?
NGitLab.Models.FileArchiveQuery.Format.set -> void
NGitLab.Models.FileArchiveQuery.Path.get -> string
NGitLab.Models.FileArchiveQuery.Path.set -> void
NGitLab.Models.FileArchiveQuery.Ref.get -> string
NGitLab.Models.FileArchiveQuery.Ref.set -> void
NGitLab.Models.FileData
NGitLab.Models.FileData.BlobId -> string
NGitLab.Models.FileData.CommitId -> string
Expand Down

0 comments on commit 93889b4

Please sign in to comment.