[FEAT]: Adds codespaces APIs

This commit is contained in:
Alexander Sklar
2023-06-16 10:26:39 -07:00
committed by GitHub
parent 8f0b3a7537
commit da5c4d7d3c
17 changed files with 480 additions and 1 deletions

View File

@@ -0,0 +1,20 @@
using System;
using System.Threading.Tasks;
namespace Octokit.Reactive
{
/// <summary>
/// A client for GitHub's Codespaces API.
/// </summary>
/// <remarks>
/// See the codespaces API documentation for more information.
/// </remarks>
public interface IObservableCodespacesClient
{
IObservable<CodespacesCollection> GetAll();
IObservable<CodespacesCollection> GetForRepository(string owner, string repo);
IObservable<Codespace> Get(string codespaceName);
IObservable<Codespace> Start(string codespaceName);
IObservable<Codespace> Stop(string codespaceName);
}
}

View File

@@ -42,5 +42,6 @@ namespace Octokit.Reactive
IObservableRateLimitClient RateLimit { get; }
IObservableMetaClient Meta { get; }
IObservableActionsClient Actions { get; }
IObservableCodespacesClient Codespaces { get; }
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Reactive.Threading.Tasks;
namespace Octokit.Reactive
{
public class ObservableCodespacesClient : IObservableCodespacesClient
{
private ICodespacesClient _client;
private IConnection _connection;
public ObservableCodespacesClient(IGitHubClient githubClient)
{
_client = githubClient.Codespaces;
_connection = githubClient.Connection;
}
public IObservable<Codespace> Get(string codespaceName)
{
Ensure.ArgumentNotNull(codespaceName, nameof(codespaceName));
return _client.Get(codespaceName).ToObservable();
}
public IObservable<CodespacesCollection> GetAll()
{
return _client.GetAll().ToObservable();
}
public IObservable<CodespacesCollection> GetForRepository(string owner, string repo)
{
Ensure.ArgumentNotNull(owner, nameof(owner));
Ensure.ArgumentNotNull(repo, nameof(repo));
return _client.GetForRepository(owner, repo).ToObservable();
}
public IObservable<Codespace> Start(string codespaceName)
{
Ensure.ArgumentNotNull(codespaceName, nameof(codespaceName));
return _client.Start(codespaceName).ToObservable();
}
public IObservable<Codespace> Stop(string codespaceName)
{
Ensure.ArgumentNotNull(codespaceName, nameof(codespaceName));
return _client.Stop(codespaceName).ToObservable();
}
}
}

View File

@@ -57,6 +57,7 @@ namespace Octokit.Reactive
RateLimit = new ObservableRateLimitClient(gitHubClient);
Meta = new ObservableMetaClient(gitHubClient);
Actions = new ObservableActionsClient(gitHubClient);
Codespaces = new ObservableCodespacesClient(gitHubClient);
}
public IConnection Connection
@@ -105,6 +106,7 @@ namespace Octokit.Reactive
public IObservableMetaClient Meta { get; private set; }
public IObservableActionsClient Actions { get; private set; }
public IObservableCodespacesClient Codespaces { get; private set; }
/// <summary>
/// Gets the latest API Info - this will be null if no API calls have been made
/// </summary>

View File

@@ -0,0 +1,59 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Octokit;
using Octokit.Tests.Helpers;
using Octokit.Tests.Integration;
using Xunit;
public class CodespacesClientTests
{
readonly ICodespacesClient _fixture;
public CodespacesClientTests()
{
var github = Helper.GetAuthenticatedClient();
_fixture = github.Codespaces;
}
[IntegrationTest]
public async Task CanGetCodespaces()
{
var retrieved = await _fixture.GetAll();
Assert.NotNull(retrieved);
}
[IntegrationTest]
public async Task CanGetCodespacesForRepo()
{
var retrieved = await _fixture.GetForRepository(Helper.UserName, Helper.RepositoryWithCodespaces);
Assert.NotNull(retrieved);
}
[IntegrationTest]
public async Task CanGetCodespaceByName()
{
var collection = await _fixture.GetForRepository(Helper.UserName, Helper.RepositoryWithCodespaces);
var codespaceName = collection.Codespaces.First().Name;
var retrieved = await _fixture.Get(codespaceName);
Assert.NotNull(retrieved);
}
[IntegrationTest]
public async Task CanStartCodespace()
{
var collection = await _fixture.GetForRepository(Helper.UserName, Helper.RepositoryWithCodespaces);
var codespaceName = collection.Codespaces.First().Name;
var retrieved = await _fixture.Start(codespaceName);
Assert.NotNull(retrieved);
}
[IntegrationTest]
public async Task CanStopCodespace()
{
var collection = await _fixture.GetForRepository(Helper.UserName, Helper.RepositoryWithCodespaces);
var codespaceName = collection.Codespaces.First().Name;
var retrieved = await _fixture.Stop(codespaceName);
Assert.NotNull(retrieved);
}
}

View File

@@ -171,6 +171,11 @@ namespace Octokit.Tests.Integration
get { return Environment.GetEnvironmentVariable("OCTOKIT_GITHUBAPP_SLUG"); }
}
public static string RepositoryWithCodespaces
{
get { return Environment.GetEnvironmentVariable("OCTOKIT_REPOSITORY_WITH_CODESPACES"); }
}
public static void DeleteRepo(IConnection connection, Repository repository)
{
if (repository != null)

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using NSubstitute;
using Octokit.Internal;
using Octokit;
using Octokit.Tests;
using Xunit;
using static Octokit.Internal.TestSetup;
public class CodespacesClientTests
{
public class TheCtor
{
[Fact]
public void EnsuresNonNullArguments()
{
Assert.Throws<ArgumentNullException>(() => new CodespacesClient(null));
}
}
public class TheGetAllMethod
{
[Fact]
public void RequestsCorrectGetAllUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new CodespacesClient(connection);
client.GetAll();
connection.Received().Get<CodespacesCollection>(Arg.Is<Uri>(u => u.ToString() == "user/codespaces"));
}
[Fact]
public void RequestsCorrectGetForRepositoryUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new CodespacesClient(connection);
client.GetForRepository("owner", "repo");
connection.Received().Get<CodespacesCollection>(Arg.Is<Uri>(u => u.ToString() == "repos/owner/repo/codespaces"));
}
[Fact]
public void RequestsCorrectGetUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new CodespacesClient(connection);
client.Get("codespaceName");
connection.Received().Get<Codespace>(Arg.Is<Uri>(u => u.ToString() == "user/codespaces/codespaceName"));
}
[Fact]
public void RequestsCorrectStartUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new CodespacesClient(connection);
client.Start("codespaceName");
connection.Received().Post<Codespace>(Arg.Is<Uri>(u => u.ToString() == "user/codespaces/codespaceName/start"));
}
[Fact]
public void RequestsCorrectStopUrl()
{
var connection = Substitute.For<IApiConnection>();
var client = new CodespacesClient(connection);
client.Stop("codespaceName");
connection.Received().Post<Codespace>(Arg.Is<Uri>(u => u.ToString() == "user/codespaces/codespaceName/stop"));
}
}
}

View File

@@ -0,0 +1,49 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
namespace Octokit
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class Codespace
{
public int Id { get; private set; }
public string Name { get; private set; }
public User Owner { get; private set; }
public User BillableOwner { get; private set; }
public Repository Repository { get; private set; }
public Machine Machine { get; private set; }
public DateTime CreatedAt { get;private set; }
public DateTime UpdatedAt { get; private set; }
public DateTime LastUsedAt { get; private set; }
public StringEnum<CodespaceState> State { get; private set; }
public string Url { get; private set; }
public string MachinesUrl { get; private set; }
public string WebUrl { get; private set; }
public string StartUrl { get; private set; }
public string StopUrl { get; private set; }
public Codespace(int id, string name, User owner, User billableOwner, Repository repository, Machine machine, DateTime createdAt, DateTime updatedAt, DateTime lastUsedAt, StringEnum<CodespaceState> state, string url, string machinesUrl, string webUrl, string startUrl, string stopUrl)
{
Id = id;
Name = name;
Owner = owner;
BillableOwner = billableOwner;
Repository = repository;
Machine = machine;
CreatedAt = createdAt;
UpdatedAt = updatedAt;
LastUsedAt = lastUsedAt;
State = state;
Url = url;
MachinesUrl = machinesUrl;
WebUrl = webUrl;
StartUrl = startUrl;
StopUrl = stopUrl;
}
public Codespace() { }
internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "Codespace: Id: {0}", Id);
}
}

View File

@@ -0,0 +1,42 @@
using Octokit.Internal;
namespace Octokit
{
public enum CodespaceState
{
[Parameter(Value = "Unknown")]
Unknown,
[Parameter(Value = "Created")]
Created,
[Parameter(Value = "Queued")]
Queued,
[Parameter(Value = "Provisioning")]
Provisioning,
[Parameter(Value = "Available")]
Available,
[Parameter(Value = "Awaiting")]
Awaiting,
[Parameter(Value = "Unavailable")]
Unavailable,
[Parameter(Value = "Deleted")]
Deleted,
[Parameter(Value = "Moved")]
Moved,
[Parameter(Value = "Shutdown")]
Shutdown,
[Parameter(Value = "Archived")]
Archived,
[Parameter(Value = "Starting")]
Starting,
[Parameter(Value = "ShuttingDown")]
ShuttingDown,
[Parameter(Value = "Failed")]
Failed,
[Parameter(Value = "Exporting")]
Exporting,
[Parameter(Value = "Updating")]
Updating,
[Parameter(Value = "Rebuilding")]
Rebuilding,
}
}

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Octokit
{
/// <summary>
/// A client for GitHub's Codespaces API.
/// Gets and creates Codespaces.
/// </summary>
/// <remarks>
/// See the <a href="https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28">Codespaces API documentation</a> for more information.
/// </remarks>
public class CodespacesClient : ApiClient, ICodespacesClient
{
/// <summary>
/// Instantiates a new GitHub Codespaces API client.
/// </summary>
/// <param name="apiConnection"></param>
public CodespacesClient(IApiConnection apiConnection) : base(apiConnection)
{
}
/// <summary>
/// Returns all the codespaces for the authenticated user.
/// </summary>
/// <returns>A codespaces collection</returns>
[ManualRoute("GET", "/user/codespaces")]
public Task<CodespacesCollection> GetAll()
{
return ApiConnection.Get<CodespacesCollection>(ApiUrls.Codespaces());
}
/// <summary>
/// Returns all the codespaces for the specified repository.
/// </summary>
/// <param name="owner"></param>
/// <param name="repo"></param>
/// <returns>A codespaces collection</returns>
[ManualRoute("GET", "/repos/{owner}/{repo}/codespaces")]
public Task<CodespacesCollection> GetForRepository(string owner, string repo)
{
return ApiConnection.Get<CodespacesCollection>(ApiUrls.CodespacesForRepository(owner, repo));
}
/// <summary>
/// Gets a codespace for the authenticated user.
/// </summary>
/// <param name="codespaceName"></param>
/// <returns>A codespace</returns>
[ManualRoute("GET", "/user/codespaces/{codespace_name}")]
public Task<Codespace> Get(string codespaceName)
{
return ApiConnection.Get<Codespace>(ApiUrls.Codespace(codespaceName));
}
/// <summary>
/// Starts a codespace for the authenticated user.
/// </summary>
/// <param name="codespaceName"></param>
/// <returns></returns>
[ManualRoute("POST", "/user/codespaces/{codespace_name}/start")]
public Task<Codespace> Start(string codespaceName)
{
return ApiConnection.Post<Codespace>(ApiUrls.CodespaceStart(codespaceName));
}
/// <summary>
/// Stops a codespace for the authenticated user.
/// </summary>
/// <param name="codespaceName"></param>
/// <returns></returns>
[ManualRoute("POST", "/user/codespaces/{codespace_name}/stop")]
public Task<Codespace> Stop(string codespaceName)
{
return ApiConnection.Post<Codespace>(ApiUrls.CodespaceStop(codespaceName));
}
}
}

View File

@@ -0,0 +1,26 @@
using Octokit.Internal;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
namespace Octokit
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class CodespacesCollection
{
public CodespacesCollection(IReadOnlyList<Codespace> codespaces, int count)
{
Codespaces = codespaces;
Count = count;
}
public CodespacesCollection() { }
[Parameter(Key = "total_count")]
public int Count { get; private set; }
[Parameter(Key = "codespaces")]
public IReadOnlyList<Codespace> Codespaces { get; private set; } = new List<Codespace>();
internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "CodespacesCollection: Count: {0}", Count);
}
}

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Octokit
{
public interface ICodespacesClient
{
Task<CodespacesCollection> GetAll();
Task<CodespacesCollection> GetForRepository(string owner, string repo);
Task<Codespace> Get(string codespaceName);
Task<Codespace> Start(string codespaceName);
Task<Codespace> Stop(string codespaceName);
}
}

View File

@@ -0,0 +1,30 @@
using System.Diagnostics;
using System.Globalization;
namespace Octokit
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class Machine
{
public string Name { get; private set; }
public string DisplayName { get; private set; }
public string OperatingSystem { get; private set; }
public long StorageInBytes { get; private set; }
public long MemoryInBytes { get; private set; }
public long CpuCount { get; private set; }
public Machine(string name, string displayName, string operatingSystem, long storageInBytes, long memoryInBytes, long cpuCount)
{
Name = name;
DisplayName = displayName;
OperatingSystem = operatingSystem;
StorageInBytes = storageInBytes;
MemoryInBytes = memoryInBytes;
CpuCount = cpuCount;
}
public Machine() { }
internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "Machine: {0}", DisplayName);
}
}

View File

@@ -120,6 +120,7 @@ namespace Octokit
RateLimit = new RateLimitClient(apiConnection);
Meta = new MetaClient(apiConnection);
Actions = new ActionsClient(apiConnection);
Codespaces = new CodespacesClient(apiConnection);
}
/// <summary>
@@ -393,6 +394,8 @@ namespace Octokit
/// </remarks>
public IActionsClient Actions { get; private set; }
public ICodespacesClient Codespaces { get; private set; }
static Uri FixUpBaseUri(Uri uri)
{
Ensure.ArgumentNotNull(uri, nameof(uri));

View File

@@ -17,6 +17,7 @@ namespace Octokit
static readonly Uri _currentUserNotificationsEndpoint = new Uri("notifications", UriKind.Relative);
static readonly Uri _currentUserAllIssues = new Uri("issues", UriKind.Relative);
static readonly Uri _currentUserOwnedAndMemberIssues = new Uri("user/issues", UriKind.Relative);
static readonly Uri _currentUserAllCodespaces = new Uri("user/codespaces", UriKind.Relative);
/// <summary>
/// Returns the <see cref="Uri"/> that returns all public repositories in
@@ -5447,5 +5448,29 @@ namespace Octokit
return "orgs/{0}/actions/runner-groups/{1}/repositories".FormatUri(org, runnerGroupId);
}
public static Uri Codespaces()
{
return _currentUserAllCodespaces;
}
public static Uri CodespacesForRepository(string owner, string repo)
{
return "repos/{0}/{1}/codespaces".FormatUri(owner, repo);
}
public static Uri Codespace(string codespaceName)
{
return "user/codespaces/{0}".FormatUri(codespaceName);
}
public static Uri CodespaceStart(string codespaceName)
{
return "user/codespaces/{0}/start".FormatUri(codespaceName);
}
public static Uri CodespaceStop(string codespaceName)
{
return "user/codespaces/{0}/stop".FormatUri(codespaceName);
}
}
}

View File

@@ -215,5 +215,7 @@ namespace Octokit
/// </remarks>
ILicensesClient Licenses { get; }
IEmojisClient Emojis { get; }
ICodespacesClient Codespaces { get; }
}
}

View File

@@ -118,4 +118,6 @@ if (AskYesNoQuestion "Do you wish to enable GitHub Enterprise (GHE) Integration
VerifyEnvironmentVariable "GitHub Enterprise application ClientID" "OCTOKIT_GHE_CLIENTID" $true
VerifyEnvironmentVariable "GitHub Enterprise application Secret" "OCTOKIT_GHE_CLIENTSECRET" $true
}
}
VerifyEnvironmentVariable "Repository with codespaces" "OCTOKIT_REPOSITORY_WITH_CODESPACES" $true