Skip to content

Commit

Permalink
Merge branch 'main' into issues-long
Browse files Browse the repository at this point in the history
  • Loading branch information
nickfloyd authored Dec 27, 2024
2 parents 2915976 + f9fb116 commit de7999e
Show file tree
Hide file tree
Showing 30 changed files with 492 additions and 42 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,7 @@ tools/*
coverage-results/*

# Rider
**/.idea/*
**/.idea/*

# macOS
.DS_Store
8 changes: 7 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,11 @@
"files.insertFinalNewline": true,
"editor.detectIndentation": false,
"editor.tabSize": 2,
"editor.insertSpaces": true
"editor.insertSpaces": true,
"[csharp]": {
"editor.tabSize": 4
},
"explorer.fileNesting.patterns": {
"*.cs": "I${capture}.cs",
},
}
8 changes: 0 additions & 8 deletions Octokit.AsyncPaginationExtension/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,6 @@ public static IPaginatedList<Collaborator> GetAllAsync(this IRepoCollaboratorsCl
public static IPaginatedList<Collaborator> GetAllAsync(this IRepoCollaboratorsClient t, long repositoryId, RepositoryCollaboratorListRequest request, int pageSize = DEFAULT_PAGE_SIZE)
=> pageSize > 0 ? new PaginatedList<Collaborator>(options => t.GetAll(repositoryId, request, options), pageSize) : throw new ArgumentOutOfRangeException(nameof(pageSize), pageSize, "The page size must be positive.");

/// <inheritdoc cref="IActionsSelfHostedRunnerGroupsClient.ListAllRunnerGroupOrganizationsForEnterprise(string, long, ApiOptions)"/>
public static IPaginatedList<Organization> ListAllRunnerGroupOrganizationsForEnterpriseAsync(this IActionsSelfHostedRunnerGroupsClient t, string enterprise, long runnerGroupId, int pageSize = DEFAULT_PAGE_SIZE)
=> pageSize > 0 ? new PaginatedList<Organization>(options => t.ListAllRunnerGroupOrganizationsForEnterprise(enterprise, runnerGroupId, options), pageSize) : throw new ArgumentOutOfRangeException(nameof(pageSize), pageSize, "The page size must be positive.");

/// <inheritdoc cref="IActionsSelfHostedRunnerGroupsClient.ListAllRunnerGroupRepositoriesForOrganization(string, long, ApiOptions)"/>
public static IPaginatedList<Repository> ListAllRunnerGroupRepositoriesForOrganizationAsync(this IActionsSelfHostedRunnerGroupsClient t, string org, long runnerGroupId, int pageSize = DEFAULT_PAGE_SIZE)
=> pageSize > 0 ? new PaginatedList<Repository>(options => t.ListAllRunnerGroupRepositoriesForOrganization(org, runnerGroupId, options), pageSize) : throw new ArgumentOutOfRangeException(nameof(pageSize), pageSize, "The page size must be positive.");

/// <inheritdoc cref="IProjectColumnsClient.GetAll(int, ApiOptions)"/>
public static IPaginatedList<ProjectColumn> GetAllAsync(this IProjectColumnsClient t, int projectId, int pageSize = DEFAULT_PAGE_SIZE)
=> pageSize > 0 ? new PaginatedList<ProjectColumn>(options => t.GetAll(projectId, options), pageSize) : throw new ArgumentOutOfRangeException(nameof(pageSize), pageSize, "The page size must be positive.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public interface IObservableActionsSelfHostedRunnerGroupsClient
/// </remarks>
/// <param name="enterprise">The enterprise name</param>
/// <param name="runnerGroupId">The runner group id</param>
IObservable<Organization> ListAllRunnerGroupOrganizationsForEnterprise(string enterprise, long runnerGroupId);
IObservable<OrganizationsResponse> ListAllRunnerGroupOrganizationsForEnterprise(string enterprise, long runnerGroupId);

/// <summary>
/// List organization access to a self-hosted runner group in an enterprise
Expand All @@ -128,7 +128,7 @@ public interface IObservableActionsSelfHostedRunnerGroupsClient
/// <param name="enterprise">The enterprise name</param>
/// <param name="runnerGroupId">The runner group id</param>
/// <param name="options">Options for changing the API response</param>
IObservable<Organization> ListAllRunnerGroupOrganizationsForEnterprise(string enterprise, long runnerGroupId, ApiOptions options);
IObservable<OrganizationsResponse> ListAllRunnerGroupOrganizationsForEnterprise(string enterprise, long runnerGroupId, ApiOptions options);

/// <summary>
/// List repository access to a self-hosted runner group in an organization
Expand All @@ -138,7 +138,7 @@ public interface IObservableActionsSelfHostedRunnerGroupsClient
/// </remarks>
/// <param name="org">The organization name</param>
/// <param name="runnerGroupId">The runner group id</param>
IObservable<Repository> ListAllRunnerGroupRepositoriesForOrganization(string org, long runnerGroupId);
IObservable<RepositoriesResponse> ListAllRunnerGroupRepositoriesForOrganization(string org, long runnerGroupId);

/// <summary>
/// List repository access to a self-hosted runner group in an organization
Expand All @@ -149,7 +149,7 @@ public interface IObservableActionsSelfHostedRunnerGroupsClient
/// <param name="org">The organization name</param>
/// <param name="runnerGroupId">The runner group id</param>
/// <param name="options">Options for changing the API response</param>
IObservable<Repository> ListAllRunnerGroupRepositoriesForOrganization(string org, long runnerGroupId, ApiOptions options);
IObservable<RepositoriesResponse> ListAllRunnerGroupRepositoriesForOrganization(string org, long runnerGroupId, ApiOptions options);

}
}
5 changes: 5 additions & 0 deletions Octokit.Reactive/Clients/IObservableMetaClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ namespace Octokit.Reactive
/// </remarks>
public interface IObservableMetaClient
{
/// <summary>
/// Returns a client to get public keys for validating request signatures.
/// </summary>
IObservablePublicKeysClient PublicKeys { get; }

/// <summary>
/// Retrieves information about GitHub.com, the service or a GitHub Enterprise installation.
/// </summary>
Expand Down
20 changes: 20 additions & 0 deletions Octokit.Reactive/Clients/IObservablePublicKeysClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace Octokit.Reactive
{
/// <summary>
/// A client for GitHub's meta public keys API.
/// </summary>
/// <remarks>
/// See the <a href="https://docs.github.com/code-security/secret-scanning/secret-scanning-partner-program#implement-signature-verification-in-your-secret-alert-service">Secret scanning documentation</a> for more details.
/// </remarks>
public interface IObservablePublicKeysClient
{
/// <summary>
/// Retrieves public keys for validating request signatures.
/// </summary>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>An <see cref="MetaPublicKeys"/> containing public keys for validating request signatures.</returns>
IObservable<MetaPublicKeys> Get(PublicKeyType keysType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public IObservable<RunnerResponse> ListAllRunnersForOrganizationRunnerGroup(stri
/// </remarks>
/// <param name="enterprise">The enterprise name</param>
/// <param name="runnerGroupId">The runner group ID</param>
public IObservable<Organization> ListAllRunnerGroupOrganizationsForEnterprise(string enterprise, long runnerGroupId)
public IObservable<OrganizationsResponse> ListAllRunnerGroupOrganizationsForEnterprise(string enterprise, long runnerGroupId)
{
return ListAllRunnerGroupOrganizationsForEnterprise(enterprise, runnerGroupId, ApiOptions.None);
}
Expand All @@ -189,12 +189,12 @@ public IObservable<Organization> ListAllRunnerGroupOrganizationsForEnterprise(st
/// <param name="enterprise">The enterprise name</param>
/// <param name="runnerGroupId">The runner group ID</param>
/// <param name="options">Options for changing the API response</param>
public IObservable<Organization> ListAllRunnerGroupOrganizationsForEnterprise(string enterprise, long runnerGroupId, ApiOptions options)
public IObservable<OrganizationsResponse> ListAllRunnerGroupOrganizationsForEnterprise(string enterprise, long runnerGroupId, ApiOptions options)
{
Ensure.ArgumentNotNullOrEmptyString(enterprise, nameof(enterprise));
Ensure.ArgumentNotNull(options, nameof(options));

return _connection.GetAndFlattenAllPages<Organization>(ApiUrls.ActionsListEnterpriseRunnerGroupOrganizations(enterprise, runnerGroupId), options);
return _client.ListAllRunnerGroupOrganizationsForEnterprise(enterprise, runnerGroupId, options).ToObservable();
}

/// <summary>
Expand All @@ -205,7 +205,7 @@ public IObservable<Organization> ListAllRunnerGroupOrganizationsForEnterprise(st
/// </remarks>
/// <param name="org">The organization name</param>
/// <param name="runnerGroupId">The runner group ID</param>
public IObservable<Repository> ListAllRunnerGroupRepositoriesForOrganization(string org, long runnerGroupId)
public IObservable<RepositoriesResponse> ListAllRunnerGroupRepositoriesForOrganization(string org, long runnerGroupId)
{
return ListAllRunnerGroupRepositoriesForOrganization(org, runnerGroupId, ApiOptions.None);
}
Expand All @@ -219,12 +219,12 @@ public IObservable<Repository> ListAllRunnerGroupRepositoriesForOrganization(str
/// <param name="org">The organization name</param>
/// <param name="runnerGroupId">The runner group ID</param>
/// <param name="options">Options for changing the API response</param>
public IObservable<Repository> ListAllRunnerGroupRepositoriesForOrganization(string org, long runnerGroupId, ApiOptions options)
public IObservable<RepositoriesResponse> ListAllRunnerGroupRepositoriesForOrganization(string org, long runnerGroupId, ApiOptions options)
{
Ensure.ArgumentNotNullOrEmptyString(org, nameof(org));
Ensure.ArgumentNotNull(options, nameof(options));

return _connection.GetAndFlattenAllPages<Repository>(ApiUrls.ActionsListOrganizationRunnerGroupRepositories(org, runnerGroupId), options);
return _client.ListAllRunnerGroupRepositoriesForOrganization(org, runnerGroupId, options).ToObservable();
}

}
Expand Down
7 changes: 7 additions & 0 deletions Octokit.Reactive/Clients/ObservableMetaClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ public ObservableMetaClient(IGitHubClient client)
{
Ensure.ArgumentNotNull(client, nameof(client));

PublicKeys = new ObservablePublicKeysClient(client);

_client = client.Meta;
}

/// <summary>
/// Returns a client to manage get public keys for validating request signatures.
/// </summary>
public IObservablePublicKeysClient PublicKeys { get; private set; }

/// <summary>
/// Retrieves information about GitHub.com, the service or a GitHub Enterprise installation.
/// </summary>
Expand Down
34 changes: 34 additions & 0 deletions Octokit.Reactive/Clients/ObservablePublicKeysClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;

namespace Octokit.Reactive
{
/// <summary>
/// A client for GitHub's public keys API.
/// </summary>
/// <remarks>
/// See the <a href="https://docs.github.com/code-security/secret-scanning/secret-scanning-partner-program#implement-signature-verification-in-your-secret-alert-service">Secret scanning documentation</a> for more details.
/// </remarks>
public class ObservablePublicKeysClient : IObservablePublicKeysClient
{
private readonly IPublicKeysClient _client;

public ObservablePublicKeysClient(IGitHubClient client)
{
Ensure.ArgumentNotNull(client, nameof(client));

_client = client.Meta.PublicKeys;
}

/// <summary>
/// Retrieves public keys for validating request signatures.
/// </summary>
/// <exception cref="ApiException">Thrown when a general API error occurs.</exception>
/// <returns>An <see cref="MetaPublicKeys"/> containing public keys for validating request signatures.</returns>
public IObservable<MetaPublicKeys> Get(PublicKeyType keysType)
{
return _client.Get(keysType).ToObservable();
}
}
}
4 changes: 2 additions & 2 deletions Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>

Expand Down
28 changes: 28 additions & 0 deletions Octokit.Tests.Integration/Clients/PublicKeysClientTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Threading.Tasks;
using Xunit;

namespace Octokit.Tests.Integration.Clients
{
public class PublicKeysClientTests
{
public class TheGetMethod
{
[IntegrationTest]
public async Task CanRetrievePublicKeys()
{
var github = Helper.GetAnonymousClient();

var result = await github.Meta.PublicKeys.Get(PublicKeyType.SecretScanning);

Assert.NotNull(result);
Assert.Equal(2, result.PublicKeys.Count);

Assert.NotNull(result.PublicKeys[0].KeyIdentifier);
Assert.NotNull(result.PublicKeys[0].Key);

Assert.NotNull(result.PublicKeys[1].KeyIdentifier);
Assert.NotNull(result.PublicKeys[1].Key);
}
}
}
}
4 changes: 2 additions & 2 deletions Octokit.Tests.Integration/Octokit.Tests.Integration.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@

<ItemGroup>
<PackageReference Include="GitHubJwt" Version="0.0.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public async Task RequestsCorrectUrl()

await client.ListAllRunnerGroupOrganizationsForEnterprise("fake", 1);

connection.Received().GetAll<Organization>(
connection.Received().GetAll<OrganizationsResponse>(
Arg.Is<Uri>(u => u.ToString() == "enterprises/fake/actions/runner-groups/1/organizations"), Args.ApiOptions);
}

Expand Down Expand Up @@ -248,7 +248,7 @@ public async Task RequestsCorrectUrl()

await client.ListAllRunnerGroupRepositoriesForOrganization("fake", 1, ApiOptions.None);

connection.Received().GetAll<Repository>(
connection.Received().GetAll<RepositoriesResponse>(
Arg.Is<Uri>(u => u.ToString() == "orgs/fake/actions/runner-groups/1/repositories"), Args.ApiOptions);
}

Expand Down
90 changes: 90 additions & 0 deletions Octokit.Tests/Clients/PublicKeysClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.Threading.Tasks;
using NSubstitute;
using Xunit;

namespace Octokit.Tests.Clients
{
public class PublicKeysClientTests
{
public class TheCtor
{
[Fact]
public void EnsuresNonNullArguments()
{
Assert.Throws<ArgumentNullException>(() => new PublicKeysClient(null));
}
}

public class TheGetMethod
{
[Fact]
public async Task RequestsTheCorrectUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new PublicKeysClient(connection);

await client.Get(PublicKeyType.CopilotApi);

connection.Received()
.Get<MetaPublicKeys>(Arg.Is<Uri>(u => u.ToString() == "meta/public_keys/copilot_api"));
}

[Fact]
public async Task RequestsCopilotApiPublicKeysEndpoint()
{
var publicKeys = new MetaPublicKeys(publicKeys: new[] {
new MetaPublicKey("4fe6b016179b74078ade7581abf4e84fb398c6fae4fb973972235b84fcd70ca3", "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELPuPiLVQbHY/clvpNnY+0BzYIXgo\nS0+XhEkTWUZEEznIVpS3rQseDTG6//gEWr4j9fY35+dGOxwOx3Z9mK3i7w==\n-----END PUBLIC KEY-----\n", true),
new MetaPublicKey("df3454252d91570ae1bc597182d1183c7a8d42ff0ae96e0f2be4ba278d776546", "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEl5xbyr5bmETCJzqAvDnYl1ZKJrkf\n89Nyq5j06TTKrnHXXDw4FYNY1uF2S/w6EOaxbf9BxOidCLvjJ8ZgKzNpww==\n-----END PUBLIC KEY-----\n", false)
});

var apiConnection = Substitute.For<IApiConnection>();
apiConnection.Get<MetaPublicKeys>(Arg.Is<Uri>(u => u.ToString() == "meta/public_keys/copilot_api")).Returns(Task.FromResult(publicKeys));

var client = new PublicKeysClient(apiConnection);

var result = await client.Get(PublicKeyType.CopilotApi);

Assert.Equal(2, result.PublicKeys.Count);
Assert.Equal("4fe6b016179b74078ade7581abf4e84fb398c6fae4fb973972235b84fcd70ca3", result.PublicKeys[0].KeyIdentifier);
Assert.Equal("-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELPuPiLVQbHY/clvpNnY+0BzYIXgo\nS0+XhEkTWUZEEznIVpS3rQseDTG6//gEWr4j9fY35+dGOxwOx3Z9mK3i7w==\n-----END PUBLIC KEY-----\n", result.PublicKeys[0].Key);
Assert.True(result.PublicKeys[0].IsCurrent);

Assert.Equal("df3454252d91570ae1bc597182d1183c7a8d42ff0ae96e0f2be4ba278d776546", result.PublicKeys[1].KeyIdentifier);
Assert.Equal("-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEl5xbyr5bmETCJzqAvDnYl1ZKJrkf\n89Nyq5j06TTKrnHXXDw4FYNY1uF2S/w6EOaxbf9BxOidCLvjJ8ZgKzNpww==\n-----END PUBLIC KEY-----\n", result.PublicKeys[1].Key);
Assert.False(result.PublicKeys[1].IsCurrent);

apiConnection.Received()
.Get<MetaPublicKeys>(Arg.Is<Uri>(u => u.ToString() == "meta/public_keys/copilot_api"));
}

[Fact]
public async Task RequestSecretScanningPublicKeysEndpoint()
{
var publicKeys = new MetaPublicKeys(publicKeys: new[] {
new MetaPublicKey("90a421169f0a406205f1563a953312f0be898d3c7b6c06b681aa86a874555f4a", "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9MJJHnMfn2+H4xL4YaPDA4RpJqUq\nkCmRCBnYERxZanmcpzQSXs1X/AljlKkbJ8qpVIW4clayyef9gWhFbNHWAA==\n-----END PUBLIC KEY-----\n", false),
new MetaPublicKey("bcb53661c06b4728e59d897fb6165d5c9cda0fd9cdf9d09ead458168deb7518c", "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYAGMWO8XgCamYKMJS6jc/qgvSlAd\nAjPuDPRcXU22YxgBrz+zoN19MzuRyW87qEt9/AmtoNP5GrobzUvQSyJFVw==\n-----END PUBLIC KEY-----\n", true)
});

var apiConnection = Substitute.For<IApiConnection>();
apiConnection.Get<MetaPublicKeys>(Arg.Is<Uri>(u => u.ToString() == "meta/public_keys/secret_scanning")).Returns(Task.FromResult(publicKeys));

var client = new PublicKeysClient(apiConnection);

var result = await client.Get(PublicKeyType.SecretScanning);

Assert.Equal(2, result.PublicKeys.Count);
Assert.Equal("90a421169f0a406205f1563a953312f0be898d3c7b6c06b681aa86a874555f4a", result.PublicKeys[0].KeyIdentifier);
Assert.Equal("-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9MJJHnMfn2+H4xL4YaPDA4RpJqUq\nkCmRCBnYERxZanmcpzQSXs1X/AljlKkbJ8qpVIW4clayyef9gWhFbNHWAA==\n-----END PUBLIC KEY-----\n", result.PublicKeys[0].Key);
Assert.False(result.PublicKeys[0].IsCurrent);

Assert.Equal("bcb53661c06b4728e59d897fb6165d5c9cda0fd9cdf9d09ead458168deb7518c", result.PublicKeys[1].KeyIdentifier);
Assert.Equal("-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYAGMWO8XgCamYKMJS6jc/qgvSlAd\nAjPuDPRcXU22YxgBrz+zoN19MzuRyW87qEt9/AmtoNP5GrobzUvQSyJFVw==\n-----END PUBLIC KEY-----\n", result.PublicKeys[1].Key);
Assert.True(result.PublicKeys[1].IsCurrent);

apiConnection.Received()
.Get<MetaPublicKeys>(Arg.Is<Uri>(u => u.ToString() == "meta/public_keys/secret_scanning"));
}
}
}
}
Loading

0 comments on commit de7999e

Please sign in to comment.