From 131ba87e3f386dc14ff3515ae1c98bb849ad7d29 Mon Sep 17 00:00:00 2001 From: Keegan Campbell Date: Thu, 20 Oct 2022 14:59:31 -0700 Subject: [PATCH] Replay #2221: Implement GitHub Actions Secrets API for both Organization and Repository (#2598) * created the interface and models for the repository secrets client * created a repository actions client to sit between repository and secrets for future extensibility * created the repository secret client and supporting objects to enable data transfer * created object for create or update secret body and made fixes to pass unit tests * created repository action unit tests * created unit tests for RepositorySecretsClient * removed set from secrets interface * fixed docs and added observable actions client * added Actions to repository client * created IObservable repository secrets client * fixed property in wrong interface fixed wrong Ctor unit test * created repository decrets reactive tests and clients * created organization actions and scerets classes and made them available through the oprganizations client * fixed intellisense text * removed uneeded getall call after return type change * created organization secret client and classes to support it * created the observable org secrets client and fixed a typo in a method name * added more ensure checks * removed unused xml doc setting * created the unit tests for the organization secrets client fixed broken unit test for repository secrets client * created observable organization actions and secrets client unit tests * added sodium.core to the integration tests to test secret creation * fixed keyid type * added actions client integration test classes (empty since the class currently doesn't have any native methods) * fixed deserialization issue * changed property name for deserialization issues * added doc for repoid on orginzation secrets url generator * created integration tests for repository and organization secrets * changed how return occurs for setting list of repos for secret * fixed some names and removed reset org name * created integration tests for observable org secrets client * removed default org value * created the integration tests for the observable repository secrets client * removed default owner project value * fixed unit tests * Update links to new docs site * Update doc links to new docs site * Update docs links to new docs site * Fix doc link to point to new docs site * Update links to new docs site * Update doc links to new docs site * Update docs links * Update docs * Update docs * Update doc links * Update docs * Update doc links * Update doc links * Update doc links * updated documentation links in actions and secrets clients * Update Octokit/Models/Response/SecretsPublicKey.cs Removing line for consistency. Co-authored-by: Thomas Hughes * Update Octokit/Models/Response/RepositorySecret.cs Removing line for consistency. Co-authored-by: Thomas Hughes * set default owner and repo * switched to using the Helper.Organization from a ORG constant set at the top of the file * swapped out variable at top of file for the Helper.Organization property * switched to helper method to create new repositories * Protected setters --> private setters in response models * RepositorySecret needs protected setters Co-authored-by: Mike Tolly Co-authored-by: Thomas Hughes Co-authored-by: mptolly-takeda <61791994+mptolly-takeda@users.noreply.github.com> --- .../IObservableOrganizationActionsClient.cs | 23 ++ .../IObservableOrganizationSecretsClient.cs | 119 +++++++ .../Clients/IObservableOrganizationsClient.cs | 5 + .../Clients/IObservableRepositoriesClient.cs | 8 + .../IObservableRepositoryActionsClient.cs | 23 ++ .../IObservableRepositorySecretsClient.cs | 79 +++++ .../ObservableOrganizationActionsClient.cs | 40 +++ .../ObservableOrganizationSecretsClient.cs | 193 ++++++++++++ .../Clients/ObservableOrganizationsClient.cs | 7 + .../Clients/ObservableRepositoriesClient.cs | 9 + .../ObservableRepositoryActionsClient.cs | 36 +++ .../ObservableRepositorySecretsClient.cs | 128 ++++++++ .../Clients/OrganizationActionsClientTests.cs | 10 + .../Clients/OrganizationSecretsClientTests.cs | 255 +++++++++++++++ .../Clients/RepositoryActionsClientTests.cs | 10 + .../Clients/RespositorySecretsClientTests.cs | 128 ++++++++ .../Octokit.Tests.Integration.csproj | 2 +- ...bservableOrganizationActionsClientTests.cs | 10 + ...bservableOrganizationSecretsClientTests.cs | 290 ++++++++++++++++++ .../ObservableRepositoryActionsClientTests.cs | 10 + .../ObservableRepositorySecretsClientTests.cs | 138 +++++++++ .../Clients/OrganizationActionsClientTests.cs | 17 + .../Clients/OrganizationSecretsClientTests.cs | 286 +++++++++++++++++ .../Clients/RepositoryActionsClientTests.cs | 17 + .../Clients/RepositorySecretsClientTests.cs | 171 +++++++++++ ...bservableOrganizationActionsClientTests.cs | 20 ++ ...bservableOrganizationSecretsClientTests.cs | 282 +++++++++++++++++ .../ObservableRepositoryActionsClientTests.cs | 20 ++ .../ObservableRepositorySecretsClientTests.cs | 170 ++++++++++ Octokit/Clients/IOrganizationActionsClient.cs | 23 ++ Octokit/Clients/IOrganizationSecretsClient.cs | 118 +++++++ Octokit/Clients/IOrganizationsClient.cs | 5 + Octokit/Clients/IRepositoriesClient.cs | 12 +- Octokit/Clients/IRepositoryActionsClient.cs | 23 ++ Octokit/Clients/IRepositorySecretsClient.cs | 77 +++++ Octokit/Clients/OrganizationActionsClient.cs | 32 ++ Octokit/Clients/OrganizationSecretsClient.cs | 194 ++++++++++++ Octokit/Clients/OrganizationsClient.cs | 6 + Octokit/Clients/RepositoriesClient.cs | 9 + Octokit/Clients/RepositoryActionsClient.cs | 32 ++ Octokit/Clients/RepositorySecretsClient.cs | 134 ++++++++ Octokit/Helpers/ApiUrls.cs | 92 ++++++ .../Request/SelectedRepositoryCollection.cs | 32 ++ .../Request/UpsertOrganizationSecret.cs | 38 +++ .../Models/Request/UpsertRepositorySecret.cs | 35 +++ Octokit/Models/Response/OrganizationSecret.cs | 39 +++ .../OrganizationSecretRepositoryCollection.cs | 38 +++ .../Response/OrganizationSecretsCollection.cs | 38 +++ Octokit/Models/Response/RepositorySecret.cs | 50 +++ .../Response/RepositorySecretsCollection.cs | 36 +++ Octokit/Models/Response/SecretsPublicKey.cs | 41 +++ 51 files changed, 3607 insertions(+), 3 deletions(-) create mode 100644 Octokit.Reactive/Clients/IObservableOrganizationActionsClient.cs create mode 100644 Octokit.Reactive/Clients/IObservableOrganizationSecretsClient.cs create mode 100644 Octokit.Reactive/Clients/IObservableRepositoryActionsClient.cs create mode 100644 Octokit.Reactive/Clients/IObservableRepositorySecretsClient.cs create mode 100644 Octokit.Reactive/Clients/ObservableOrganizationActionsClient.cs create mode 100644 Octokit.Reactive/Clients/ObservableOrganizationSecretsClient.cs create mode 100644 Octokit.Reactive/Clients/ObservableRepositoryActionsClient.cs create mode 100644 Octokit.Reactive/Clients/ObservableRepositorySecretsClient.cs create mode 100644 Octokit.Tests.Integration/Clients/OrganizationActionsClientTests.cs create mode 100644 Octokit.Tests.Integration/Clients/OrganizationSecretsClientTests.cs create mode 100644 Octokit.Tests.Integration/Clients/RepositoryActionsClientTests.cs create mode 100644 Octokit.Tests.Integration/Clients/RespositorySecretsClientTests.cs create mode 100644 Octokit.Tests.Integration/Reactive/ObservableOrganizationActionsClientTests.cs create mode 100644 Octokit.Tests.Integration/Reactive/ObservableOrganizationSecretsClientTests.cs create mode 100644 Octokit.Tests.Integration/Reactive/ObservableRepositoryActionsClientTests.cs create mode 100644 Octokit.Tests.Integration/Reactive/ObservableRepositorySecretsClientTests.cs create mode 100644 Octokit.Tests/Clients/OrganizationActionsClientTests.cs create mode 100644 Octokit.Tests/Clients/OrganizationSecretsClientTests.cs create mode 100644 Octokit.Tests/Clients/RepositoryActionsClientTests.cs create mode 100644 Octokit.Tests/Clients/RepositorySecretsClientTests.cs create mode 100644 Octokit.Tests/Reactive/ObservableOrganizationActionsClientTests.cs create mode 100644 Octokit.Tests/Reactive/ObservableOrganizationSecretsClientTests.cs create mode 100644 Octokit.Tests/Reactive/ObservableRepositoryActionsClientTests.cs create mode 100644 Octokit.Tests/Reactive/ObservableRepositorySecretsClientTests.cs create mode 100644 Octokit/Clients/IOrganizationActionsClient.cs create mode 100644 Octokit/Clients/IOrganizationSecretsClient.cs create mode 100644 Octokit/Clients/IRepositoryActionsClient.cs create mode 100644 Octokit/Clients/IRepositorySecretsClient.cs create mode 100644 Octokit/Clients/OrganizationActionsClient.cs create mode 100644 Octokit/Clients/OrganizationSecretsClient.cs create mode 100644 Octokit/Clients/RepositoryActionsClient.cs create mode 100644 Octokit/Clients/RepositorySecretsClient.cs create mode 100644 Octokit/Models/Request/SelectedRepositoryCollection.cs create mode 100644 Octokit/Models/Request/UpsertOrganizationSecret.cs create mode 100644 Octokit/Models/Request/UpsertRepositorySecret.cs create mode 100644 Octokit/Models/Response/OrganizationSecret.cs create mode 100644 Octokit/Models/Response/OrganizationSecretRepositoryCollection.cs create mode 100644 Octokit/Models/Response/OrganizationSecretsCollection.cs create mode 100644 Octokit/Models/Response/RepositorySecret.cs create mode 100644 Octokit/Models/Response/RepositorySecretsCollection.cs create mode 100644 Octokit/Models/Response/SecretsPublicKey.cs diff --git a/Octokit.Reactive/Clients/IObservableOrganizationActionsClient.cs b/Octokit.Reactive/Clients/IObservableOrganizationActionsClient.cs new file mode 100644 index 0000000000..f3d00a623c --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableOrganizationActionsClient.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Org Actions API. + /// + /// + /// See the Actions API documentation for more information. + /// + public interface IObservableOrganizationActionsClient + { + /// + /// Returns a client to manage organization secrets. + /// + /// + /// See the Secrets API documentation for more information. + /// + IObservableOrganizationSecretsClient Secrets { get; } + } +} diff --git a/Octokit.Reactive/Clients/IObservableOrganizationSecretsClient.cs b/Octokit.Reactive/Clients/IObservableOrganizationSecretsClient.cs new file mode 100644 index 0000000000..205dfeb229 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableOrganizationSecretsClient.cs @@ -0,0 +1,119 @@ +using System; +using System.Reactive; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Organization Secrets API. + /// + /// + /// See the Organization Secrets API documentation for more details. + /// + public interface IObservableOrganizationSecretsClient + { + /// + /// Get the public signing key to encrypt secrets for an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// Thrown when a general API error occurs. + /// A instance for the organization public key. + IObservable GetPublicKey(string org); + + /// + /// List the secrets for an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// Thrown when a general API error occurs. + /// A instance for the list of organization secrets. + IObservable GetAll(string org); + + /// + /// Get a secret from an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + /// A instance for the organization secret. + IObservable Get(string org, string secretName); + + /// + /// Create or update a secret in an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The encrypted value, id of the encryption key, and visibility info to upsert + /// Thrown when a general API error occurs. + /// A instance for the organization secret that was created or updated. + IObservable CreateOrUpdate(string org, string secretName, UpsertOrganizationSecret upsertSecret); + + /// + /// Delete a secret in an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + IObservable Delete(string org, string secretName); + + /// + /// Get the list of selected sites that have access to a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + IObservable GetSelectedRepositoriesForSecret(string org, string secretName); + + /// + /// Set the list of selected sites that have access to a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The list of repositories that should have access to view and use the secret + /// Thrown when a general API error occurs. + IObservable SetSelectedRepositoriesForSecret(string org, string secretName, SelectedRepositoryCollection repositories); + + /// + /// Add a selected site to the visibility list of a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The id of the repo to add to the visibility list of the secret + /// Thrown when a general API error occurs. + IObservable AddRepoToOrganizationSecret(string org, string secretName, long repoId); + + /// + /// ARemoved a selected site from the visibility list of a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The id of the repo to add to the visibility list of the secret + /// Thrown when a general API error occurs. + IObservable RemoveRepoFromOrganizationSecret(string org, string secretName, long repoId); + } +} diff --git a/Octokit.Reactive/Clients/IObservableOrganizationsClient.cs b/Octokit.Reactive/Clients/IObservableOrganizationsClient.cs index 8e7596c7c5..d44a187990 100644 --- a/Octokit.Reactive/Clients/IObservableOrganizationsClient.cs +++ b/Octokit.Reactive/Clients/IObservableOrganizationsClient.cs @@ -26,6 +26,11 @@ public interface IObservableOrganizationsClient /// IObservableOrganizationOutsideCollaboratorsClient OutsideCollaborator { get; } + /// + /// Returns a client to manage organization actions. + /// + IObservableOrganizationActionsClient Actions { get; } + /// /// Returns the specified organization. /// diff --git a/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs b/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs index 562ea50de9..169f86e2ac 100644 --- a/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs +++ b/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs @@ -195,6 +195,14 @@ public interface IObservableRepositoriesClient /// A of . IObservable GetAllForOrg(string organization, ApiOptions options); + /// + /// Access GitHub's Repository Actions API. + /// + /// + /// See the API documentation for more details. + /// + IObservableRepositoryActionsClient Actions { get; } + /// /// Client for managing branches in a repository. /// diff --git a/Octokit.Reactive/Clients/IObservableRepositoryActionsClient.cs b/Octokit.Reactive/Clients/IObservableRepositoryActionsClient.cs new file mode 100644 index 0000000000..6f649e0ef4 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableRepositoryActionsClient.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Repository Actions API. + /// + /// + /// See the Repository Actions API documentation for more details. + /// + public interface IObservableRepositoryActionsClient + { + /// + /// Client for GitHub's Repository Actions API + /// + /// + /// See the Deployments API documentation for more details + /// + IObservableRepositorySecretsClient Secrets { get; } + } +} diff --git a/Octokit.Reactive/Clients/IObservableRepositorySecretsClient.cs b/Octokit.Reactive/Clients/IObservableRepositorySecretsClient.cs new file mode 100644 index 0000000000..0d346d0b31 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableRepositorySecretsClient.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Reactive; +using System.Text; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Repository Secrets API. + /// + /// + /// See the Repository Secrets API documentation for more details. + /// + public interface IObservableRepositorySecretsClient + { + /// + /// Get the public signing key to encrypt secrets for a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown when a general API error occurs. + /// A instance for the repository public key. + IObservable GetPublicKey(string owner, string repoName); + + /// + /// List the secrets for a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown when a general API error occurs. + /// A instance for the list of repository secrets. + IObservable GetAll(string owner, string repoName); + + /// + /// Get a secret from a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// Thrown when a general API error occurs. + /// A instance for the repository secret. + IObservable Get(string owner, string repoName, string secretName); + + /// + /// Create or update a secret in a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// The encrypted value and id of the encryption key + /// Thrown when a general API error occurs. + /// A instance for the repository secret that was created or updated. + IObservable CreateOrUpdate(string owner, string repoName, string secretName, UpsertRepositorySecret upsertSecret); + + /// + /// Delete a secret in a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// Thrown when a general API error occurs. + IObservable Delete(string owner, string repoName, string secretName); + } +} diff --git a/Octokit.Reactive/Clients/ObservableOrganizationActionsClient.cs b/Octokit.Reactive/Clients/ObservableOrganizationActionsClient.cs new file mode 100644 index 0000000000..833a88514a --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableOrganizationActionsClient.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Org Actions API. + /// + /// + /// See the Actions API documentation for more information. + /// + public class ObservableOrganizationActionsClient : IObservableOrganizationActionsClient + { + readonly IOrganizationActionsClient _client; + readonly IConnection _connection; + + /// + /// Initializes a new Organization API client. + /// + /// An used to make the requests + public ObservableOrganizationActionsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + Secrets = new ObservableOrganizationSecretsClient(client); + + _client = client.Organization.Actions; + _connection = client.Connection; + } + + /// + /// Returns a client to manage organization secrets. + /// + /// + /// See the Secrets API documentation for more information. + /// + public IObservableOrganizationSecretsClient Secrets { get; private set; } + } +} diff --git a/Octokit.Reactive/Clients/ObservableOrganizationSecretsClient.cs b/Octokit.Reactive/Clients/ObservableOrganizationSecretsClient.cs new file mode 100644 index 0000000000..d18febef65 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableOrganizationSecretsClient.cs @@ -0,0 +1,193 @@ +using System; +using System.Reactive; +using System.Reactive.Threading.Tasks; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Organization Secrets API. + /// + /// + /// See the Organization Secrets API documentation for more details. + /// + public class ObservableOrganizationSecretsClient : IObservableOrganizationSecretsClient + { + readonly IOrganizationSecretsClient _client; + readonly IConnection _connection; + + /// + /// Initializes a new Organization API client. + /// + /// An used to make the requests + public ObservableOrganizationSecretsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Organization.Actions.Secrets; + _connection = client.Connection; + } + + /// + /// Get the public signing key to encrypt secrets for an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// Thrown when a general API error occurs. + /// A instance for the organization public key. + public IObservable GetPublicKey(string org) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + + return _client.GetPublicKey(org).ToObservable(); + } + + /// + /// List the secrets for an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// Thrown when a general API error occurs. + /// A instance for the list of organization secrets. + public IObservable GetAll(string org) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + + return _client.GetAll(org).ToObservable(); + } + + /// + /// Get a secret from an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + /// A instance for the organization secret. + public IObservable Get(string org, string secretName) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + + return _client.Get(org, secretName).ToObservable(); + } + + /// + /// Create or update a secret in an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The encrypted value, id of the encryption key, and visibility info to upsert + /// Thrown when a general API error occurs. + /// A instance for the organization secret that was created or updated. + public IObservable CreateOrUpdate(string org, string secretName, UpsertOrganizationSecret upsertSecret) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + Ensure.ArgumentNotNull(upsertSecret, nameof(upsertSecret)); + Ensure.ArgumentNotNull(upsertSecret.EncryptedValue, nameof(upsertSecret.EncryptedValue)); + Ensure.ArgumentNotNull(upsertSecret.KeyId, nameof(upsertSecret.KeyId)); + + return _client.CreateOrUpdate(org, secretName, upsertSecret).ToObservable(); + } + + /// + /// Delete a secret in an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + public IObservable Delete(string org, string secretName) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + + return _client.Delete(org, secretName).ToObservable(); + } + + /// + /// Get the list of selected sites that have access to a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + public IObservable GetSelectedRepositoriesForSecret(string org, string secretName) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + + return _client.GetSelectedRepositoriesForSecret(org, secretName).ToObservable(); + } + + /// + /// Set the list of selected sites that have access to a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The list of repositories that should have access to view and use the secret + /// Thrown when a general API error occurs. + public IObservable SetSelectedRepositoriesForSecret(string org, string secretName, SelectedRepositoryCollection repositories) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + Ensure.ArgumentNotNull(repositories, nameof(repositories)); + + return _client.SetSelectedRepositoriesForSecret(org, secretName, repositories).ToObservable(); + } + + /// + /// Add a selected site to the visibility list of a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The id of the repo to add to the visibility list of the secret + /// Thrown when a general API error occurs. + public IObservable AddRepoToOrganizationSecret(string org, string secretName, long repoId) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + Ensure.ArgumentNotNull(repoId, nameof(repoId)); + + return _client.AddRepoToOrganizationSecret(org, secretName, repoId).ToObservable(); + } + + /// + /// ARemoved a selected site from the visibility list of a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The id of the repo to add to the visibility list of the secret + /// Thrown when a general API error occurs. + public IObservable RemoveRepoFromOrganizationSecret(string org, string secretName, long repoId) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + Ensure.ArgumentNotNull(repoId, nameof(repoId)); + + return _client.RemoveRepoFromOrganizationSecret(org, secretName, repoId).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableOrganizationsClient.cs b/Octokit.Reactive/Clients/ObservableOrganizationsClient.cs index 348dc49e83..e90bac920f 100644 --- a/Octokit.Reactive/Clients/ObservableOrganizationsClient.cs +++ b/Octokit.Reactive/Clients/ObservableOrganizationsClient.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using Octokit.Reactive.Internal; @@ -21,6 +22,7 @@ public ObservableOrganizationsClient(IGitHubClient client) Team = new ObservableTeamsClient(client); Hook = new ObservableOrganizationHooksClient(client); OutsideCollaborator = new ObservableOrganizationOutsideCollaboratorsClient(client); + Actions = new ObservableOrganizationActionsClient(client); _client = client.Organization; _connection = client.Connection; @@ -47,6 +49,11 @@ public ObservableOrganizationsClient(IGitHubClient client) /// public IObservableOrganizationOutsideCollaboratorsClient OutsideCollaborator { get; private set; } + /// + /// Returns a client to manage organization actions. + /// + public IObservableOrganizationActionsClient Actions { get; private set; } + /// /// Returns the specified organization. /// diff --git a/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs b/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs index 13bb249fc2..199fc6fa00 100644 --- a/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs +++ b/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs @@ -40,6 +40,7 @@ public ObservableRepositoriesClient(IGitHubClient client) Invitation = new ObservableRepositoryInvitationsClient(client); Traffic = new ObservableRepositoryTrafficClient(client); Project = new ObservableProjectsClient(client); + Actions = new ObservableRepositoryActionsClient(client); } /// @@ -805,6 +806,14 @@ public IObservable Compare(string owner, string name, string @bas return _client.Commit.Compare(owner, name, @base, head).ToObservable(); } + /// + /// A client for GitHub's Repository Actions API. + /// + /// + /// See the Actions API documentation for more details + /// + public IObservableRepositoryActionsClient Actions { get; private set; } + /// /// A client for GitHub's Repository Branches API. /// diff --git a/Octokit.Reactive/Clients/ObservableRepositoryActionsClient.cs b/Octokit.Reactive/Clients/ObservableRepositoryActionsClient.cs new file mode 100644 index 0000000000..631bbf4bf4 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableRepositoryActionsClient.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Repository Actions API. + /// + /// + /// See the Repository Actions API documentation for more details. + /// + public class ObservableRepositoryActionsClient : IObservableRepositoryActionsClient + { + readonly IRepositoryActionsClient _client; + readonly IConnection _connection; + + public ObservableRepositoryActionsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Repository.Actions; + _connection = client.Connection; + + Secrets = new ObservableRepositorySecretsClient(client); + } + + /// + /// Client for GitHub's Repository Actions API + /// + /// + /// See the Deployments API documentation for more details + /// + public IObservableRepositorySecretsClient Secrets { get; private set; } + } +} diff --git a/Octokit.Reactive/Clients/ObservableRepositorySecretsClient.cs b/Octokit.Reactive/Clients/ObservableRepositorySecretsClient.cs new file mode 100644 index 0000000000..fa11987edf --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableRepositorySecretsClient.cs @@ -0,0 +1,128 @@ +using Octokit.Reactive.Internal; +using System; +using System.Collections.Generic; +using System.Reactive; +using System.Reactive.Threading.Tasks; +using System.Text; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Repository Secrets API. + /// + /// + /// See the Repository Secrets API documentation for more details. + /// + public class ObservableRepositorySecretsClient : IObservableRepositorySecretsClient + { + readonly IRepositorySecretsClient _client; + readonly IConnection _connection; + + public ObservableRepositorySecretsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, nameof(client)); + + _client = client.Repository.Actions.Secrets; + _connection = client.Connection; + } + + /// + /// Get the public signing key to encrypt secrets for a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown when a general API error occurs. + /// A instance for the repository public key. + public IObservable GetPublicKey(string owner, string repoName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repoName, nameof(repoName)); + + return _client.GetPublicKey(owner, repoName).ToObservable(); + } + + /// + /// List the secrets for a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown when a general API error occurs. + /// A instance for the list of repository secrets. + public IObservable GetAll(string owner, string repoName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repoName, nameof(repoName)); + + return _client.GetAll(owner, repoName).ToObservable(); + } + + /// + /// Get a secret from a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// Thrown when a general API error occurs. + /// A instance for the repository secret. + public IObservable Get(string owner, string repoName, string secretName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repoName, nameof(repoName)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + + return _client.Get(owner, repoName, secretName).ToObservable(); + } + + /// + /// Create or update a secret in a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// The encrypted value and id of the encryption key + /// Thrown when a general API error occurs. + /// A instance for the repository secret that was created or updated. + public IObservable CreateOrUpdate(string owner, string repoName, string secretName, UpsertRepositorySecret upsertSecret) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repoName, nameof(repoName)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + Ensure.ArgumentNotNull(upsertSecret, nameof(upsertSecret)); + Ensure.ArgumentNotNullOrEmptyString(upsertSecret.EncryptedValue, nameof(upsertSecret.EncryptedValue)); + Ensure.ArgumentNotNullOrEmptyString(upsertSecret.KeyId, nameof(upsertSecret.KeyId)); + + return _client.CreateOrUpdate(owner, repoName, secretName, upsertSecret).ToObservable(); + } + + /// + /// Delete a secret in a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// Thrown when a general API error occurs. + public IObservable Delete(string owner, string repoName, string secretName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repoName, nameof(repoName)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + + return _client.Delete(owner, repoName, secretName).ToObservable(); + } + } +} diff --git a/Octokit.Tests.Integration/Clients/OrganizationActionsClientTests.cs b/Octokit.Tests.Integration/Clients/OrganizationActionsClientTests.cs new file mode 100644 index 0000000000..20548d7b1f --- /dev/null +++ b/Octokit.Tests.Integration/Clients/OrganizationActionsClientTests.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit.Tests.Integration.Clients +{ + public class OrganizationActionsClientTests + { + } +} diff --git a/Octokit.Tests.Integration/Clients/OrganizationSecretsClientTests.cs b/Octokit.Tests.Integration/Clients/OrganizationSecretsClientTests.cs new file mode 100644 index 0000000000..ab93991787 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/OrganizationSecretsClientTests.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using System.Linq; + +#if SODIUM_CORE_AVAILABLE +using Sodium; +#endif + +namespace Octokit.Tests.Integration.Clients +{ + public class OrganizationSecretsClientTests + { + + public class GetPublicKeyMethod + { + [OrganizationTest] + public async Task GetPublicKey() + { + var github = Helper.GetAuthenticatedClient(); + + var key = await github.Organization.Actions.Secrets.GetPublicKey(Helper.Organization); + + Assert.True(!string.IsNullOrWhiteSpace(key.KeyId)); + } + } + + public class GetAllMethod + { + [OrganizationTest] + public async Task GetSecrets() + { + var github = Helper.GetAuthenticatedClient(); + + var secrets = await github.Organization.Actions.Secrets.GetAll(Helper.Organization); + + Assert.NotEmpty(secrets.Secrets); + } + } + + /// + /// Please create a secret in your specific repo called TEST + /// + public class GetMethod + { + [OrganizationTest] + public async Task GetSecret() + { + var github = Helper.GetAuthenticatedClient(); + + var secret = await github.Organization.Actions.Secrets.Get(Helper.Organization, "TEST"); + + Assert.NotNull(secret); + Assert.True(secret.Name == "TEST"); + } + } + + public class CreateOrUpdateMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task UpsertSecret() + { + var github = Helper.GetAuthenticatedClient(); + var now = DateTime.Now; + + var publicKey = await github.Organization.Actions.Secrets.GetPublicKey(Helper.Organization); + var upsertValue = GetSecretForCreate("value", publicKey); + + var secret = await github.Organization.Actions.Secrets.CreateOrUpdate(Helper.Organization, "UPSERT_TEST", upsertValue); + + Assert.NotNull(secret); + Assert.True(secret.UpdatedAt > now); + } +#endif + } + + public class DeleteMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task DeleteSecret() + { + var github = Helper.GetAuthenticatedClient(); + + var secretName = "DELETE_TEST"; + + var publicKey = await github.Organization.Actions.Secrets.GetPublicKey(Helper.Organization); + var upsertValue = GetSecretForCreate("value", publicKey); + + await github.Organization.Actions.Secrets.CreateOrUpdate(Helper.Organization, secretName, upsertValue); + await github.Organization.Actions.Secrets.Delete(Helper.Organization, secretName); + + } +#endif + } + + public class GetSelectedRepositoriesForSecretMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task GetSelectedRepositoriesForSecret() + { + var github = Helper.GetAuthenticatedClient(); + + var secretName = "LIST_SELECTED_REPO_TEST"; + + var repo = await CreateRepoIfNotExists(github, "list-secrets-selected-repo-test"); + + var key = await github.Organization.Actions.Secrets.GetPublicKey(Helper.Organization); + var upsertSecret = GetSecretForCreate("secret", key, new Repository[] { repo }); + var secret = await github.Organization.Actions.Secrets.CreateOrUpdate(Helper.Organization, secretName, upsertSecret); + + var visibilityRepos = await github.Organization.Actions.Secrets.GetSelectedRepositoriesForSecret(Helper.Organization, secretName); + + Assert.NotEmpty(visibilityRepos.Repositories); + } +#endif + } + + public class SetSelectedRepositoriesForSecretMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task SetSelectedRepositoriesForSecret() + { + var github = Helper.GetAuthenticatedClient(); + + var secretName = "SET_SELECTED_REPO_TEST"; + + var repo1 = await CreateRepoIfNotExists(github, "set-secrets-selected-repo-test-1"); + var repo2 = await CreateRepoIfNotExists(github, "set-secrets-selected-repo-test-2"); + + var key = await github.Organization.Actions.Secrets.GetPublicKey(Helper.Organization); + var upsertSecret = GetSecretForCreate("secret", key, new Repository[] { repo1 }); + await github.Organization.Actions.Secrets.CreateOrUpdate(Helper.Organization, secretName, upsertSecret); + + await github.Organization.Actions.Secrets.SetSelectedRepositoriesForSecret(Helper.Organization, secretName, new SelectedRepositoryCollection(new long[] { repo1.Id, repo2.Id })); + + var visibilityRepos = await github.Organization.Actions.Secrets.GetSelectedRepositoriesForSecret(Helper.Organization, secretName); + + Assert.NotEmpty(visibilityRepos.Repositories); + Assert.Equal(2, visibilityRepos.Count); + } +#endif + } + + public class AddRepoToOrganizationSecretMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task AddSelectedRepositoriesForSecret() + { + var github = Helper.GetAuthenticatedClient(); + + var secretName = "ADD_SELECTED_REPO_TEST"; + + var repo1 = await CreateRepoIfNotExists(github, "add-secrets-selected-repo-test-1"); + var repo2 = await CreateRepoIfNotExists(github, "add-secrets-selected-repo-test-2"); + + var key = await github.Organization.Actions.Secrets.GetPublicKey(Helper.Organization); + var upsertSecret = GetSecretForCreate("secret", key, new Repository[] { repo1 }); + await github.Organization.Actions.Secrets.CreateOrUpdate(Helper.Organization, secretName, upsertSecret); + + await github.Organization.Actions.Secrets.AddRepoToOrganizationSecret(Helper.Organization, secretName, repo2.Id); + + var visibilityRepos = await github.Organization.Actions.Secrets.GetSelectedRepositoriesForSecret(Helper.Organization, secretName); + + Assert.NotEmpty(visibilityRepos.Repositories); + Assert.Equal(2, visibilityRepos.Count); + } +#endif + } + + public class RemoveRepoFromOrganizationSecretMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task RemoveSelectedRepositoriesForSecret() + { + var github = Helper.GetAuthenticatedClient(); + + var secretName = "REMOVE_SELECTED_REPO_TEST"; + + var repo1 = await CreateRepoIfNotExists(github, "remove-secrets-selected-repo-test-1"); + var repo2 = await CreateRepoIfNotExists(github, "remove-secrets-selected-repo-test-2"); + + var key = await github.Organization.Actions.Secrets.GetPublicKey(Helper.Organization); + var upsertSecret = GetSecretForCreate("secret", key, new Repository[] { repo1, repo2 }); + await github.Organization.Actions.Secrets.CreateOrUpdate(Helper.Organization, secretName, upsertSecret); + + await github.Organization.Actions.Secrets.RemoveRepoFromOrganizationSecret(Helper.Organization, secretName, repo2.Id); + + var visibilityRepos = await github.Organization.Actions.Secrets.GetSelectedRepositoriesForSecret(Helper.Organization, secretName); + + Assert.NotEmpty(visibilityRepos.Repositories); + Assert.Equal(1, visibilityRepos.Count); + } +#endif + } + +#if SODIUM_CORE_AVAILABLE + private static UpsertOrganizationSecret GetSecretForCreate(string secretValue, SecretsPublicKey key) + { + var secretBytes = Encoding.UTF8.GetBytes(secretValue); + var publicKey = Convert.FromBase64String(key.Key); + var sealedPublicKeyBox = SealedPublicKeyBox.Create(secretBytes, publicKey); + + var upsertValue = new UpsertOrganizationSecret + { + EncryptedValue = Convert.ToBase64String(sealedPublicKeyBox), + KeyId = key.KeyId, + Visibility = "all" + + }; + + return upsertValue; + } + + private static UpsertOrganizationSecret GetSecretForCreate(string secretValue, SecretsPublicKey key, Repository[] repos) + { + var secretBytes = Encoding.UTF8.GetBytes(secretValue); + var publicKey = Convert.FromBase64String(key.Key); + var sealedPublicKeyBox = SealedPublicKeyBox.Create(secretBytes, publicKey); + + var upsertValue = new UpsertOrganizationSecret + { + EncryptedValue = Convert.ToBase64String(sealedPublicKeyBox), + KeyId = key.KeyId, + Visibility = "selected", + SelectedRepositoriesIds = repos.Select(r => r.Id) + + }; + + return upsertValue; + } +#endif + + private static async Task CreateRepoIfNotExists(IGitHubClient github, string name) + { + try + { + var existingRepo = await github.Repository.Get(Helper.Organization, name); + return existingRepo; + } + catch + { + var newRepo = await github.Repository.Create(Helper.Organization, new NewRepository(name)); + return newRepo; + } + } + } +} diff --git a/Octokit.Tests.Integration/Clients/RepositoryActionsClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoryActionsClientTests.cs new file mode 100644 index 0000000000..95f60732d1 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/RepositoryActionsClientTests.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit.Tests.Integration.Clients +{ + public class RepositoryActionsClientTests + { + } +} diff --git a/Octokit.Tests.Integration/Clients/RespositorySecretsClientTests.cs b/Octokit.Tests.Integration/Clients/RespositorySecretsClientTests.cs new file mode 100644 index 0000000000..e56c2d6cc2 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/RespositorySecretsClientTests.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Versioning; +using System.Text; +using System.Threading.Tasks; +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +#if SODIUM_CORE_AVAILABLE +using Sodium; +#endif + +namespace Octokit.Tests.Integration.Clients +{ + /// + /// Access to view and update secrets is required for the following tests + /// + public class RespositorySecretsClientTests + { + /// + /// Fill these in for tests to work + /// + internal const string OWNER = "octokit"; + internal const string REPO = "octokit.net"; + + public class GetPublicKeyMethod + { + [IntegrationTest] + public async Task GetPublicKey() + { + var github = Helper.GetAuthenticatedClient(); + + var key = await github.Repository.Actions.Secrets.GetPublicKey(OWNER, REPO); + + Assert.True(!string.IsNullOrWhiteSpace(key.KeyId)); + } + } + + public class GetAllMethod + { + [IntegrationTest] + public async Task GetSecrets() + { + var github = Helper.GetAuthenticatedClient(); + + var secrets = await github.Repository.Actions.Secrets.GetAll(OWNER, REPO); + + Assert.NotEmpty(secrets.Secrets); + } + } + + /// + /// Please create a secret in your specific repo called TEST + /// + public class GetMethod + { + [IntegrationTest] + public async Task GetSecret() + { + var github = Helper.GetAuthenticatedClient(); + + var secret = await github.Repository.Actions.Secrets.Get(OWNER, REPO, "TEST"); + + Assert.NotNull(secret); + Assert.True(secret.Name == "TEST"); + } + } + + public class CreateOrUpdateMethod + { +#if SODIUM_CORE_AVAILABLE + [IntegrationTest] + public async Task UpsertSecret() + { + var github = Helper.GetAuthenticatedClient(); + var now = DateTime.Now; + + var publicKey = await github.Repository.Actions.Secrets.GetPublicKey(OWNER, REPO); + var upsertValue = GetSecretForCreate("value", publicKey); + + var secret = await github.Repository.Actions.Secrets.CreateOrUpdate(OWNER, REPO, "UPSERT_TEST", upsertValue); + + Assert.NotNull(secret); + Assert.True(secret.UpdatedAt > now); + } +#endif + } + + public class DeleteMethod + { +#if SODIUM_CORE_AVAILABLE + [IntegrationTest] + public async Task DeleteSecret() + { + var github = Helper.GetAuthenticatedClient(); + + var secretName = "DELETE_TEST"; + + var publicKey = await github.Repository.Actions.Secrets.GetPublicKey(OWNER, REPO); + var upsertValue = GetSecretForCreate("value", publicKey); + + await github.Repository.Actions.Secrets.CreateOrUpdate(OWNER, REPO, secretName, upsertValue); + await github.Repository.Actions.Secrets.Delete(OWNER, REPO, secretName); + + } +#endif + } +#if SODIUM_CORE_AVAILABLE + private static UpsertRepositorySecret GetSecretForCreate(string secretValue, SecretsPublicKey key) + { + var secretBytes = Encoding.UTF8.GetBytes(secretValue); + var publicKey = Convert.FromBase64String(key.Key); + var sealedPublicKeyBox = SealedPublicKeyBox.Create(secretBytes, publicKey); + + var upsertValue = new UpsertRepositorySecret + { + EncryptedValue = Convert.ToBase64String(sealedPublicKeyBox), + KeyId = key.KeyId + + }; + + return upsertValue; + } +#endif + } +} diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 2f00a3dfe2..2f045e2d84 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -25,7 +25,7 @@ - + diff --git a/Octokit.Tests.Integration/Reactive/ObservableOrganizationActionsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableOrganizationActionsClientTests.cs new file mode 100644 index 0000000000..e05a22c110 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableOrganizationActionsClientTests.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit.Tests.Integration.Reactive +{ + public class ObservableOrganizationActionsClientTests + { + } +} diff --git a/Octokit.Tests.Integration/Reactive/ObservableOrganizationSecretsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableOrganizationSecretsClientTests.cs new file mode 100644 index 0000000000..9cdf668ba8 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableOrganizationSecretsClientTests.cs @@ -0,0 +1,290 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Text; +using System.Threading.Tasks; +using Octokit.Reactive; +using Xunit; +using System.Linq; +using Octokit.Tests.Integration.Helpers; + +#if SODIUM_CORE_AVAILABLE +using Sodium; +#endif + +namespace Octokit.Tests.Integration.Reactive +{ + public class ObservableOrganizationSecretsClientTests + { + public class GetPublicKeyMethod + { + [OrganizationTest] + public async Task GetPublicKey() + { + var github = Helper.GetAuthenticatedClient(); + var clients = new ObservableOrganizationSecretsClient(github); + + var keyObservable = clients.GetPublicKey(Helper.Organization); + var key = await keyObservable; + + Assert.True(!string.IsNullOrWhiteSpace(key.KeyId)); + } + } + + public class GetAllMethod + { + [OrganizationTest] + public async Task GetSecrets() + { + var github = Helper.GetAuthenticatedClient(); + var clients = new ObservableOrganizationSecretsClient(github); + + var secretsObservable = clients.GetAll(Helper.Organization); + var secrets = await secretsObservable; + + Assert.NotEmpty(secrets.Secrets); + } + } + + /// + /// Please create a secret in your specific repo called TEST + /// + public class GetMethod + { + [OrganizationTest] + public async Task GetSecret() + { + var github = Helper.GetAuthenticatedClient(); + var clients = new ObservableOrganizationSecretsClient(github); + + var secretObservable = clients.Get(Helper.Organization, "TEST"); + var secret = await secretObservable; + + Assert.NotNull(secret); + Assert.True(secret.Name == "TEST"); + } + } + + public class CreateOrUpdateMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task UpsertSecret() + { + var github = Helper.GetAuthenticatedClient(); + var clients = new ObservableOrganizationSecretsClient(github); + var now = DateTime.Now; + + var keyObservable = clients.GetPublicKey(Helper.Organization); + var key = await keyObservable; + + var upsertValue = GetSecretForCreate("value", key); + + var secretObservable = clients.CreateOrUpdate(Helper.Organization, "REACTIVE_UPSERT_TEST", upsertValue); + var secret = await secretObservable; + + Assert.NotNull(secret); + Assert.True(secret.UpdatedAt > now); + } +#endif + } + + public class DeleteMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task DeleteSecret() + { + var github = Helper.GetAuthenticatedClient(); + var clients = new ObservableOrganizationSecretsClient(github); + + var secretName = "REACTIVE_DELETE_TEST"; + + var keyObservable = clients.GetPublicKey(Helper.Organization); + var key = await keyObservable; + var upsertValue = GetSecretForCreate("value", key); + + var createSecretObservable = clients.CreateOrUpdate(Helper.Organization, secretName, upsertValue); + await createSecretObservable; + + var deleteSecretObservable = clients.Delete(Helper.Organization, secretName); + await deleteSecretObservable; + } +#endif + } + + public class GetSelectedRepositoriesForSecretMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task GetSelectedRepositoriesForSecret() + { + var github = Helper.GetAuthenticatedClient(); + var clients = new ObservableOrganizationSecretsClient(github); + + var secretName = "REACTIVE_LIST_SELECTED_REPO_TEST"; + + var repoName = Helper.MakeNameWithTimestamp("reactive-add-secrets-selected-repo-test"); + var repo = await github.CreateRepositoryContext(new NewRepository(repoName)); + + var keyObservable = clients.GetPublicKey(Helper.Organization); + var key = await keyObservable; + var upsertSecret = GetSecretForCreate("value", key, new Repository[] { repo.Repository }); + + var secretObservable = clients.CreateOrUpdate(Helper.Organization, secretName, upsertSecret); + await secretObservable; + + var visibilityReposObservable = clients.GetSelectedRepositoriesForSecret(Helper.Organization, secretName); + var visibilityRepos = await visibilityReposObservable; + + Assert.NotEmpty(visibilityRepos.Repositories); + } +#endif + } + + public class SetSelectedRepositoriesForSecretMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task SetSelectedRepositoriesForSecret() + { + var github = Helper.GetAuthenticatedClient(); + var clients = new ObservableOrganizationSecretsClient(github); + + var secretName = "REACTIVE_SET_SELECTED_REPO_TEST"; + + var repo1Name = Helper.MakeNameWithTimestamp("reactive-add-secrets-selected-repo-test-1"); + var repo2Name = Helper.MakeNameWithTimestamp("reactive-add-secrets-selected-repo-test-2"); + + var repo1 = await github.CreateRepositoryContext(new NewRepository(repo1Name)); + var repo2 = await github.CreateRepositoryContext(new NewRepository(repo2Name)); + + var keyObservable = clients.GetPublicKey(Helper.Organization); + var key = await keyObservable; + var upsertSecret = GetSecretForCreate("value", key, new Repository[] { repo1.Repository }); + + var secretObservable = clients.CreateOrUpdate(Helper.Organization, secretName, upsertSecret); + await secretObservable; + + var setRepoListObservable = clients.SetSelectedRepositoriesForSecret(Helper.Organization, secretName, new SelectedRepositoryCollection(new long[] { repo1.RepositoryId, repo2.RepositoryId })); + await setRepoListObservable; + + var visibilityReposObservable = clients.GetSelectedRepositoriesForSecret(Helper.Organization, secretName); + var visibilityRepos = await visibilityReposObservable; + + Assert.NotEmpty(visibilityRepos.Repositories); + Assert.Equal(2, visibilityRepos.Count); + } +#endif + } + + public class AddRepoToOrganizationSecretMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task AddSelectedRepositoriesForSecret() + { + var github = Helper.GetAuthenticatedClient(); + var clients = new ObservableOrganizationSecretsClient(github); + + var secretName = "REACTIVE_ADD_SELECTED_REPO_TEST"; + + var repo1Name = Helper.MakeNameWithTimestamp("reactive-add-secrets-selected-repo-test-1"); + var repo2Name = Helper.MakeNameWithTimestamp("reactive-add-secrets-selected-repo-test-2"); + + var repo1 = await github.CreateRepositoryContext(new NewRepository(repo1Name)); + var repo2 = await github.CreateRepositoryContext(new NewRepository(repo2Name)); + + var keyObservable = clients.GetPublicKey(Helper.Organization); + var key = await keyObservable; + var upsertSecret = GetSecretForCreate("value", key, new Repository[] { repo1.Repository }); + + var secretObservable = clients.CreateOrUpdate(Helper.Organization, secretName, upsertSecret); + await secretObservable; + + var addRepoListObservable = clients.AddRepoToOrganizationSecret(Helper.Organization, secretName, repo2.RepositoryId); + await addRepoListObservable; + + var visibilityReposObservable = clients.GetSelectedRepositoriesForSecret(Helper.Organization, secretName); + var visibilityRepos = await visibilityReposObservable; + + Assert.NotEmpty(visibilityRepos.Repositories); + Assert.Equal(2, visibilityRepos.Count); + } +#endif + } + + public class RemoveRepoFromOrganizationSecretMethod + { +#if SODIUM_CORE_AVAILABLE + [OrganizationTest] + public async Task RemoveSelectedRepositoriesForSecret() + { + var github = Helper.GetAuthenticatedClient(); + var clients = new ObservableOrganizationSecretsClient(github); + + var secretName = "REACTIVE_REMOVE_SELECTED_REPO_TEST"; + + var repo1Name = Helper.MakeNameWithTimestamp("reactive-add-secrets-selected-repo-test-1"); + var repo2Name = Helper.MakeNameWithTimestamp("reactive-add-secrets-selected-repo-test-2"); + + var repo1 = await github.CreateRepositoryContext(new NewRepository(repo1Name)); + var repo2 = await github.CreateRepositoryContext(new NewRepository(repo2Name)); + + var keyObservable = clients.GetPublicKey(Helper.Organization); + var key = await keyObservable; + var upsertSecret = GetSecretForCreate("secret", key, new Repository[] { repo1.Repository, repo2.Repository }); + + var secretObservable = clients.CreateOrUpdate(Helper.Organization, secretName, upsertSecret); + await secretObservable; + + var removeRepoListObservable = clients.RemoveRepoFromOrganizationSecret(Helper.Organization, secretName, repo2.RepositoryId); + await removeRepoListObservable; + + var visibilityReposObservable = clients.GetSelectedRepositoriesForSecret(Helper.Organization, secretName); + var visibilityRepos = await visibilityReposObservable; + + Assert.NotEmpty(visibilityRepos.Repositories); + Assert.Equal(1, visibilityRepos.Count); + } +#endif + } + +#if SODIUM_CORE_AVAILABLE + private static UpsertOrganizationSecret GetSecretForCreate(string secretValue, SecretsPublicKey key) + { + var secretBytes = Encoding.UTF8.GetBytes(secretValue); + var publicKey = Convert.FromBase64String(key.Key); + var sealedPublicKeyBox = SealedPublicKeyBox.Create(secretBytes, publicKey); + + var upsertValue = new UpsertOrganizationSecret + { + EncryptedValue = Convert.ToBase64String(sealedPublicKeyBox), + KeyId = key.KeyId, + Visibility = "all" + + }; + + return upsertValue; + } + + private static UpsertOrganizationSecret GetSecretForCreate(string secretValue, SecretsPublicKey key, Repository[] repos) + { + var secretBytes = Encoding.UTF8.GetBytes(secretValue); + var publicKey = Convert.FromBase64String(key.Key); + var sealedPublicKeyBox = SealedPublicKeyBox.Create(secretBytes, publicKey); + + var upsertValue = new UpsertOrganizationSecret + { + EncryptedValue = Convert.ToBase64String(sealedPublicKeyBox), + KeyId = key.KeyId, + Visibility = "selected", + SelectedRepositoriesIds = repos.Select(r => r.Id) + + }; + + return upsertValue; + } +#endif + } +} diff --git a/Octokit.Tests.Integration/Reactive/ObservableRepositoryActionsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableRepositoryActionsClientTests.cs new file mode 100644 index 0000000000..3e2ca85d2e --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableRepositoryActionsClientTests.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit.Tests.Integration.Reactive +{ + public class ObservableRepositoryActionsClientTests + { + } +} diff --git a/Octokit.Tests.Integration/Reactive/ObservableRepositorySecretsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableRepositorySecretsClientTests.cs new file mode 100644 index 0000000000..bb29cd6175 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableRepositorySecretsClientTests.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Octokit.Reactive; +using System.Reactive.Linq; + +#if SODIUM_CORE_AVAILABLE +using Sodium; +#endif + +namespace Octokit.Tests.Integration.Reactive.Clients +{ + public class ObservableRepositorySecretsClientTests + { + /// + /// Fill these in for tests to work + /// + internal const string OWNER = ""; + internal const string REPO = ""; + + public class GetPublicKeyMethod + { + [IntegrationTest] + public async Task GetPublicKey() + { + var github = Helper.GetAuthenticatedClient(); + var client = new ObservableRepositorySecretsClient(github); + + var keyObservable = client.GetPublicKey(OWNER, REPO); + var key = await keyObservable; + + Assert.True(!string.IsNullOrWhiteSpace(key.KeyId)); + } + } + + public class GetAllMethod + { + [IntegrationTest] + public async Task GetSecrets() + { + var github = Helper.GetAuthenticatedClient(); + var client = new ObservableRepositorySecretsClient(github); + + var secretsObservable = client.GetAll(OWNER, REPO); + var secrets = await secretsObservable; + + Assert.NotEmpty(secrets.Secrets); + } + } + + /// + /// Please create a secret in your specific repo called TEST + /// + public class GetMethod + { + [IntegrationTest] + public async Task GetSecret() + { + var github = Helper.GetAuthenticatedClient(); + var client = new ObservableRepositorySecretsClient(github); + var secretName = "TEST"; + + var secretsObservable = client.Get(OWNER, REPO, secretName); + var secret = await secretsObservable; + + Assert.NotNull(secret); + Assert.True(secret.Name == secretName); + } + } + + public class CreateOrUpdateMethod + { +#if SODIUM_CORE_AVAILABLE + [IntegrationTest] + public async Task UpsertSecret() + { + var github = Helper.GetAuthenticatedClient(); + var client = new ObservableRepositorySecretsClient(github); + var now = DateTime.Now; + + var keyObservable = client.GetPublicKey(OWNER, REPO); + var key = await keyObservable; + var upsertValue = GetSecretForCreate("value", key); + + var secretObservable = client.CreateOrUpdate(OWNER, REPO, "REACTIVE_UPSERT_TEST", upsertValue); + var secret = await secretObservable; + + Assert.NotNull(secret); + Assert.True(secret.UpdatedAt > now); + } +#endif + } + + public class DeleteMethod + { +#if SODIUM_CORE_AVAILABLE + [IntegrationTest] + public async Task DeleteSecret() + { + var github = Helper.GetAuthenticatedClient(); + var client = new ObservableRepositorySecretsClient(github); + + var secretName = "DELETE_TEST"; + + var keyObservable = client.GetPublicKey(OWNER, REPO); + var key = await keyObservable; + var upsertValue = GetSecretForCreate("value", key); + + var createObservable = client.CreateOrUpdate(OWNER, REPO, secretName, upsertValue); + await createObservable; + + var deleteObservable = client.Delete(OWNER, REPO, secretName); + await deleteObservable; + + } +#endif + } +#if SODIUM_CORE_AVAILABLE + private static UpsertRepositorySecret GetSecretForCreate(string secretValue, SecretsPublicKey key) + { + var secretBytes = Encoding.UTF8.GetBytes(secretValue); + var publicKey = Convert.FromBase64String(key.Key); + var sealedPublicKeyBox = SealedPublicKeyBox.Create(secretBytes, publicKey); + + var upsertValue = new UpsertRepositorySecret + { + EncryptedValue = Convert.ToBase64String(sealedPublicKeyBox), + KeyId = key.KeyId + + }; + + return upsertValue; + } +#endif + } +} diff --git a/Octokit.Tests/Clients/OrganizationActionsClientTests.cs b/Octokit.Tests/Clients/OrganizationActionsClientTests.cs new file mode 100644 index 0000000000..8ac1ef66a5 --- /dev/null +++ b/Octokit.Tests/Clients/OrganizationActionsClientTests.cs @@ -0,0 +1,17 @@ +using System; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class OrganizationActionsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new OrganizationActionsClient(null)); + } + } + } +} \ No newline at end of file diff --git a/Octokit.Tests/Clients/OrganizationSecretsClientTests.cs b/Octokit.Tests/Clients/OrganizationSecretsClientTests.cs new file mode 100644 index 0000000000..da08c24e58 --- /dev/null +++ b/Octokit.Tests/Clients/OrganizationSecretsClientTests.cs @@ -0,0 +1,286 @@ +using NSubstitute; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class OrganizationSecretsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new OrganizationSecretsClient(null)); + } + } + + public class GetPublicKeyMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + await client.GetPublicKey("org"); + + connection.Received() + .Get(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/public-key")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetPublicKey(null)); + await Assert.ThrowsAsync(() => client.GetPublicKey("")); + } + } + + public class GetAllMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + await client.GetAll("org"); + + connection.Received() + .Get(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAll(null)); + await Assert.ThrowsAsync(() => client.GetAll("")); + } + } + + public class GetMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + await client.Get("org", "secret"); + + connection.Received() + .Get(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/secret")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Get(null, "secret")); + await Assert.ThrowsAsync(() => client.Get("org", null)); + + await Assert.ThrowsAsync(() => client.Get("", "secret")); + await Assert.ThrowsAsync(() => client.Get("org", "")); + } + } + + public class CreateOrUpdateMethod + { + [Fact] + public async Task PostsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + var upsertSecret = new UpsertOrganizationSecret + { + EncryptedValue = "encryptedValue", + KeyId = "keyId", + Visibility = "private" + }; + await client.CreateOrUpdate("org", "secret", upsertSecret); + + connection.Received() + .Put(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/secret"), upsertSecret); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + var upsertSecret = new UpsertOrganizationSecret + { + EncryptedValue = "encryptedValue", + KeyId = "keyId" + }; + + await Assert.ThrowsAsync(() => client.CreateOrUpdate(null, "secret", upsertSecret)); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", null, upsertSecret)); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "secret", null)); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "secret", new UpsertOrganizationSecret())); + + await Assert.ThrowsAsync(() => client.CreateOrUpdate("", "secret", upsertSecret)); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "", upsertSecret)); + } + } + + public class DeleteMethod + { + [Fact] + public async Task DeletesTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + await client.Delete("org", "secret"); + + connection.Received() + .Delete(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/secret")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Delete(null, "secret")); + await Assert.ThrowsAsync(() => client.Delete("owner", null)); + + await Assert.ThrowsAsync(() => client.Delete("", "secret")); + await Assert.ThrowsAsync(() => client.Delete("owner", "")); + } + } + + public class GetSelectedRepositoriesForSecretMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + await client.GetSelectedRepositoriesForSecret("org", "secret"); + + connection.Received() + .Get(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/secret/repositories")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetSelectedRepositoriesForSecret(null, "secret")); + await Assert.ThrowsAsync(() => client.GetSelectedRepositoriesForSecret("org", null)); + await Assert.ThrowsAsync(() => client.GetSelectedRepositoriesForSecret("", "secret")); + await Assert.ThrowsAsync(() => client.GetSelectedRepositoriesForSecret("org", "")); + } + } + + public class SetSelectedRepositoriesForSecretMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + var repoIds = new List + { + 1, + 2, + 3 + }; + var repos = new SelectedRepositoryCollection(repoIds); + + await client.SetSelectedRepositoriesForSecret("org", "secret", repos); + + connection.Received() + .Put(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/secret/repositories"), repos); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + var repoIds = new List + { + 1, + 2, + 3 + }; + var repos = new SelectedRepositoryCollection(repoIds); + + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret(null, "secret", repos)); + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret("org", null, repos)); + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret("org", "secret", null)); + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret("org", "secret", new SelectedRepositoryCollection())); + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret("", "secret", repos)); + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret("org", "", repos)); + } + } + + public class AddRepoToOrganizationSecretMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + await client.AddRepoToOrganizationSecret("org", "secret", 1); + + connection.Received() + .Put(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/secret/repositories/1")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.AddRepoToOrganizationSecret(null, "secret", 1)); + await Assert.ThrowsAsync(() => client.AddRepoToOrganizationSecret("org", null, 1)); + await Assert.ThrowsAsync(() => client.AddRepoToOrganizationSecret("", "secret", 1)); + await Assert.ThrowsAsync(() => client.AddRepoToOrganizationSecret("org", "", 1)); + } + } + + public class RemoveRepoFromOrganizationSecretMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + await client.RemoveRepoFromOrganizationSecret("org", "secret", 1); + + connection.Received() + .Delete(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/secret/repositories/1")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.RemoveRepoFromOrganizationSecret(null, "secret", 1)); + await Assert.ThrowsAsync(() => client.RemoveRepoFromOrganizationSecret("org", null, 1)); + await Assert.ThrowsAsync(() => client.RemoveRepoFromOrganizationSecret("", "secret", 1)); + await Assert.ThrowsAsync(() => client.RemoveRepoFromOrganizationSecret("org", "", 1)); + } + } + } +} diff --git a/Octokit.Tests/Clients/RepositoryActionsClientTests.cs b/Octokit.Tests/Clients/RepositoryActionsClientTests.cs new file mode 100644 index 0000000000..36f977d45d --- /dev/null +++ b/Octokit.Tests/Clients/RepositoryActionsClientTests.cs @@ -0,0 +1,17 @@ +using System; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class RepositoryActionsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new RepositoryActionsClient(null)); + } + } + } +} diff --git a/Octokit.Tests/Clients/RepositorySecretsClientTests.cs b/Octokit.Tests/Clients/RepositorySecretsClientTests.cs new file mode 100644 index 0000000000..4e8accaed9 --- /dev/null +++ b/Octokit.Tests/Clients/RepositorySecretsClientTests.cs @@ -0,0 +1,171 @@ +using NSubstitute; +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class RepositorySecretsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new RepositorySecretsClient(null)); + } + } + + public class GetPublicKeyMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new RepositorySecretsClient(connection); + + await client.GetPublicKey("owner", "repo"); + + connection.Received() + .Get(Arg.Is(u => u.ToString() == "repos/owner/repo/actions/secrets/public-key")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new RepositorySecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetPublicKey(null, "repo")); + await Assert.ThrowsAsync(() => client.GetPublicKey("owner", null)); + await Assert.ThrowsAsync(() => client.GetPublicKey("", "repo")); + await Assert.ThrowsAsync(() => client.GetPublicKey("owner", "")); + } + } + + public class GetAllMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new RepositorySecretsClient(connection); + + await client.GetAll("owner", "repo"); + + connection.Received() + .Get(Arg.Is(u => u.ToString() == "repos/owner/repo/actions/secrets")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new RepositorySecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAll(null, "repo")); + await Assert.ThrowsAsync(() => client.GetAll("owner", null)); + + await Assert.ThrowsAsync(() => client.GetAll("", "repo")); + await Assert.ThrowsAsync(() => client.GetAll("owner", "")); + } + } + + public class GetMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new RepositorySecretsClient(connection); + + await client.Get("owner", "repo", "secret"); + + connection.Received() + .Get(Arg.Is(u => u.ToString() == "repos/owner/repo/actions/secrets/secret")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new RepositorySecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Get(null, "repo", "secret")); + await Assert.ThrowsAsync(() => client.Get("owner", null, "secret")); + await Assert.ThrowsAsync(() => client.Get("owner", "repo", null)); + + await Assert.ThrowsAsync(() => client.Get("", "repo", "secret")); + await Assert.ThrowsAsync(() => client.Get("owner", "", "secret")); + await Assert.ThrowsAsync(() => client.Get("owner", "repo", "")); + } + } + + public class CreateOrUpdateMethod + { + [Fact] + public async Task PostsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new RepositorySecretsClient(connection); + var upsertSecret = new UpsertRepositorySecret + { + EncryptedValue = "encryptedValue", + KeyId = "keyId" + }; + await client.CreateOrUpdate("owner", "repo", "secret", upsertSecret); + + connection.Received() + .Put(Arg.Is(u => u.ToString() == "repos/owner/repo/actions/secrets/secret"), upsertSecret); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new RepositorySecretsClient(Substitute.For()); + + var upsertSecret = new UpsertRepositorySecret + { + EncryptedValue = "encryptedValue", + KeyId = "keyId" + }; + + await Assert.ThrowsAsync(() => client.CreateOrUpdate(null, "repo", "secret", upsertSecret)); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", null, "secret", upsertSecret)); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "repo", null, upsertSecret)); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "repo", "secret", null)); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "repo", "secret", new UpsertRepositorySecret())); + + await Assert.ThrowsAsync(() => client.CreateOrUpdate("", "repo", "secret", upsertSecret)); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "", "secret", upsertSecret)); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "repo", "", upsertSecret)); + } + } + + public class DeleteMethod + { + [Fact] + public async Task DeletesTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new RepositorySecretsClient(connection); + + await client.Delete("owner", "repo", "secret"); + + connection.Received() + .Delete(Arg.Is(u => u.ToString() == "repos/owner/repo/actions/secrets/secret")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new RepositorySecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Delete(null, "repo", "secret")); + await Assert.ThrowsAsync(() => client.Delete("owner", null, "secret")); + await Assert.ThrowsAsync(() => client.Delete("owner", "repo", null)); + + await Assert.ThrowsAsync(() => client.Delete("", "repo", "secret")); + await Assert.ThrowsAsync(() => client.Delete("owner", "", "secret")); + await Assert.ThrowsAsync(() => client.Delete("owner", "repo", "")); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableOrganizationActionsClientTests.cs b/Octokit.Tests/Reactive/ObservableOrganizationActionsClientTests.cs new file mode 100644 index 0000000000..1476c76b4f --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableOrganizationActionsClientTests.cs @@ -0,0 +1,20 @@ +using Octokit.Reactive; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableOrganizationActionsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableOrganizationActionsClient(null)); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableOrganizationSecretsClientTests.cs b/Octokit.Tests/Reactive/ObservableOrganizationSecretsClientTests.cs new file mode 100644 index 0000000000..7e36b26864 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableOrganizationSecretsClientTests.cs @@ -0,0 +1,282 @@ +using NSubstitute; +using Octokit.Reactive; +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableOrganizationSecretsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableOrganizationSecretsClient(null)); + } + } + + public class GetPublicKeyMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationSecretsClient(gitHubClient); + + await client.GetPublicKey("org"); + + gitHubClient.Received().Organization.Actions.Secrets.GetPublicKey("org"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetPublicKey(null).ToTask()); + await Assert.ThrowsAsync(() => client.GetPublicKey("").ToTask()); + } + } + + public class GetAllMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationSecretsClient(gitHubClient); + + await client.GetAll("org"); + + gitHubClient.Received().Organization.Actions.Secrets.GetAll("org"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAll(null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAll("").ToTask()); + } + } + + public class GetMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationSecretsClient(gitHubClient); + + await client.Get("org", "secret"); + + gitHubClient.Received().Organization.Actions.Secrets.Get("org", "secret"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Get(null, "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Get("org", null).ToTask()); + + await Assert.ThrowsAsync(() => client.Get("", "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Get("org", "").ToTask()); + } + } + + public class CreateOrUpdateMethod + { + [Fact] + public async Task PostsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationSecretsClient(gitHubClient); + var upsertSecret = new UpsertOrganizationSecret + { + EncryptedValue = "encryptedValue", + KeyId = "keyId", + Visibility = "private" + }; + + await client.CreateOrUpdate("org", "secret", upsertSecret); + + gitHubClient.Received().Organization.Actions.Secrets.CreateOrUpdate("org", "secret", upsertSecret); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationSecretsClient(Substitute.For()); + + var upsertSecret = new UpsertOrganizationSecret + { + EncryptedValue = "encryptedValue", + KeyId = "keyId" + }; + + await Assert.ThrowsAsync(() => client.CreateOrUpdate(null, "secret", upsertSecret).ToTask()); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", null, upsertSecret).ToTask()); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "secret", null).ToTask()); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "secret", new UpsertOrganizationSecret()).ToTask()); + + await Assert.ThrowsAsync(() => client.CreateOrUpdate("", "secret", upsertSecret).ToTask()); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "", upsertSecret).ToTask()); + } + } + + public class DeleteMethod + { + [Fact] + public async Task DeletesTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationSecretsClient(gitHubClient); + + await client.Delete("org", "secret"); + + gitHubClient.Received().Organization.Actions.Secrets.Delete("org", "secret"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Delete(null, "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Delete("owner", null).ToTask()); + + await Assert.ThrowsAsync(() => client.Delete("", "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Delete("owner", "").ToTask()); + } + } + + public class GetSelectedRepositoriesForSecretMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableOrganizationSecretsClient(gitHubClient); + + await client.GetSelectedRepositoriesForSecret("org", "secret"); + + gitHubClient.Received().Organization.Actions.Secrets.GetSelectedRepositoriesForSecret("org", "secret"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableOrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetSelectedRepositoriesForSecret(null, "secret").ToTask()); + await Assert.ThrowsAsync(() => client.GetSelectedRepositoriesForSecret("org", null).ToTask()); + await Assert.ThrowsAsync(() => client.GetSelectedRepositoriesForSecret("", "secret").ToTask()); + await Assert.ThrowsAsync(() => client.GetSelectedRepositoriesForSecret("org", "").ToTask()); + } + } + + public class SetSelectedRepositoriesForSecretMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + var repoIds = new List + { + 1, + 2, + 3 + }; + var repos = new SelectedRepositoryCollection(repoIds); + + await client.SetSelectedRepositoriesForSecret("org", "secret", repos); + + connection.Received() + .Put(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/secret/repositories"), repos); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + var repoIds = new List + { + 1, + 2, + 3 + }; + var repos = new SelectedRepositoryCollection(repoIds); + + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret(null, "secret", repos)); + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret("org", null, repos)); + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret("org", "secret", null)); + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret("org", "secret", new SelectedRepositoryCollection())); + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret("", "secret", repos)); + await Assert.ThrowsAsync(() => client.SetSelectedRepositoriesForSecret("org", "", repos)); + } + } + + public class AddRepoToOrganizationSecretMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + await client.AddRepoToOrganizationSecret("org", "secret", 1); + + connection.Received() + .Put(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/secret/repositories/1")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.AddRepoToOrganizationSecret(null, "secret", 1)); + await Assert.ThrowsAsync(() => client.AddRepoToOrganizationSecret("org", null, 1)); + await Assert.ThrowsAsync(() => client.AddRepoToOrganizationSecret("", "secret", 1)); + await Assert.ThrowsAsync(() => client.AddRepoToOrganizationSecret("org", "", 1)); + } + } + + public class RemoveRepoFromOrganizationSecretMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new OrganizationSecretsClient(connection); + + await client.RemoveRepoFromOrganizationSecret("org", "secret", 1); + + connection.Received() + .Delete(Arg.Is(u => u.ToString() == "orgs/org/actions/secrets/secret/repositories/1")); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new OrganizationSecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.RemoveRepoFromOrganizationSecret(null, "secret", 1)); + await Assert.ThrowsAsync(() => client.RemoveRepoFromOrganizationSecret("org", null, 1)); + await Assert.ThrowsAsync(() => client.RemoveRepoFromOrganizationSecret("", "secret", 1)); + await Assert.ThrowsAsync(() => client.RemoveRepoFromOrganizationSecret("org", "", 1)); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableRepositoryActionsClientTests.cs b/Octokit.Tests/Reactive/ObservableRepositoryActionsClientTests.cs new file mode 100644 index 0000000000..22b79b8fbc --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableRepositoryActionsClientTests.cs @@ -0,0 +1,20 @@ +using Octokit.Reactive; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableRepositoryActionsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableRepositoryActionsClient(null)); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableRepositorySecretsClientTests.cs b/Octokit.Tests/Reactive/ObservableRepositorySecretsClientTests.cs new file mode 100644 index 0000000000..92a57e1c2b --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableRepositorySecretsClientTests.cs @@ -0,0 +1,170 @@ +using NSubstitute; +using Octokit.Reactive; +using System; +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableRepositorySecretsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableRepositorySecretsClient(null)); + } + } + + public class GetPublicKeyMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositorySecretsClient(gitHubClient); + + await client.GetPublicKey("owner", "repo"); + + gitHubClient.Received().Repository.Actions.Secrets.GetPublicKey("owner", "repo"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableRepositorySecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetPublicKey(null, "repo").ToTask()); + await Assert.ThrowsAsync(() => client.GetPublicKey("owner", null).ToTask()); + await Assert.ThrowsAsync(() => client.GetPublicKey("", "repo").ToTask()); + await Assert.ThrowsAsync(() => client.GetPublicKey("owner", "").ToTask()); + } + } + + public class GetAllMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositorySecretsClient(gitHubClient); + + await client.GetAll("owner", "repo"); + + gitHubClient.Received().Repository.Actions.Secrets.GetAll("owner", "repo"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableRepositorySecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAll(null, "repo").ToTask()); + await Assert.ThrowsAsync(() => client.GetAll("owner", null).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAll("", "repo").ToTask()); + await Assert.ThrowsAsync(() => client.GetAll("owner", "").ToTask()); + } + } + + public class GetMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositorySecretsClient(gitHubClient); + + await client.Get("owner", "repo","secret"); + + gitHubClient.Received().Repository.Actions.Secrets.Get("owner", "repo", "secret"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableRepositorySecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Get(null, "repo", "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Get("owner", null, "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Get("owner", "repo", null).ToTask()); + + await Assert.ThrowsAsync(() => client.Get("", "repo", "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Get("owner", "", "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Get("owner", "repo", "").ToTask()); + } + } + + public class CreateOrUpdateMethod + { + [Fact] + public async Task PostsTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositorySecretsClient(gitHubClient); + var upsert = new UpsertRepositorySecret + { + EncryptedValue = "encryptedValue", + KeyId = "keyId" + }; + + await client.CreateOrUpdate("owner", "repo", "secret", upsert); + + gitHubClient.Received().Repository.Actions.Secrets.CreateOrUpdate("owner", "repo", "secret", upsert); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableRepositorySecretsClient(Substitute.For()); + + var upsertSecret = new UpsertRepositorySecret + { + EncryptedValue = "encryptedValue", + KeyId = "keyId" + }; + + await Assert.ThrowsAsync(() => client.CreateOrUpdate(null, "repo", "secret", upsertSecret).ToTask()); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", null, "secret", upsertSecret).ToTask()); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "repo", null, upsertSecret).ToTask()); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "repo", "secret", null).ToTask()); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "repo", "secret", new UpsertRepositorySecret()).ToTask()); + + await Assert.ThrowsAsync(() => client.CreateOrUpdate("", "repo", "secret", upsertSecret).ToTask()); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "", "secret", upsertSecret).ToTask()); + await Assert.ThrowsAsync(() => client.CreateOrUpdate("owner", "repo", "", upsertSecret).ToTask()); + } + } + + public class DeleteMethod + { + [Fact] + public async Task DeletesTheCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositorySecretsClient(gitHubClient); + + await client.Delete("owner", "repo", "secret"); + + gitHubClient.Received().Repository.Actions.Secrets.Delete("owner", "repo", "secret"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableRepositorySecretsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Delete(null, "repo", "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Delete("owner", null, "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Delete("owner", "repo", null).ToTask()); + + await Assert.ThrowsAsync(() => client.Delete("", "repo", "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Delete("owner", "", "secret").ToTask()); + await Assert.ThrowsAsync(() => client.Delete("owner", "repo", "").ToTask()); + } + } + } +} diff --git a/Octokit/Clients/IOrganizationActionsClient.cs b/Octokit/Clients/IOrganizationActionsClient.cs new file mode 100644 index 0000000000..38dd3cd032 --- /dev/null +++ b/Octokit/Clients/IOrganizationActionsClient.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit +{ + /// + /// A client for GitHub's Org Actions API. + /// + /// + /// See the Actions API documentation for more information. + /// + public interface IOrganizationActionsClient + { + /// + /// Returns a client to manage organization secrets. + /// + /// + /// See the Secrets API documentation for more information. + /// + IOrganizationSecretsClient Secrets { get; } + } +} diff --git a/Octokit/Clients/IOrganizationSecretsClient.cs b/Octokit/Clients/IOrganizationSecretsClient.cs new file mode 100644 index 0000000000..7966ea9a63 --- /dev/null +++ b/Octokit/Clients/IOrganizationSecretsClient.cs @@ -0,0 +1,118 @@ +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Organization Secrets API. + /// + /// + /// See the Organization Secrets API documentation for more details. + /// + public interface IOrganizationSecretsClient + { + /// + /// Get the public signing key to encrypt secrets for an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// Thrown when a general API error occurs. + /// A instance for the organization public key. + Task GetPublicKey(string org); + + /// + /// List the secrets for an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// Thrown when a general API error occurs. + /// A instance for the list of organization secrets. + Task GetAll(string org); + + /// + /// Get a secret from an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + /// A instance for the organization secret. + Task Get(string org, string secretName); + + /// + /// Create or update a secret in an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The encrypted value, id of the encryption key, and visibility info to upsert + /// Thrown when a general API error occurs. + /// A instance for the organization secret that was created or updated. + Task CreateOrUpdate(string org, string secretName, UpsertOrganizationSecret upsertSecret); + + /// + /// Delete a secret in an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + Task Delete(string org, string secretName); + + /// + /// Get the list of selected sites that have access to a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + Task GetSelectedRepositoriesForSecret(string org, string secretName); + + /// + /// Set the list of selected sites that have access to a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The list of repositories that should have access to view and use the secret + /// Thrown when a general API error occurs. + Task SetSelectedRepositoriesForSecret(string org, string secretName, SelectedRepositoryCollection repositories); + + /// + /// Add a selected site to the visibility list of a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The id of the repo to add to the visibility list of the secret + /// Thrown when a general API error occurs. + Task AddRepoToOrganizationSecret(string org, string secretName, long repoId); + + /// + /// ARemoved a selected site from the visibility list of a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The id of the repo to add to the visibility list of the secret + /// Thrown when a general API error occurs. + Task RemoveRepoFromOrganizationSecret(string org, string secretName, long repoId); + } +} diff --git a/Octokit/Clients/IOrganizationsClient.cs b/Octokit/Clients/IOrganizationsClient.cs index 5f3f2fc6fb..d87d69387d 100644 --- a/Octokit/Clients/IOrganizationsClient.cs +++ b/Octokit/Clients/IOrganizationsClient.cs @@ -35,6 +35,11 @@ public interface IOrganizationsClient /// IOrganizationOutsideCollaboratorsClient OutsideCollaborator { get; } + /// + /// Returns a client to manage organization actions. + /// + IOrganizationActionsClient Actions { get; } + /// /// Returns the specified . /// diff --git a/Octokit/Clients/IRepositoriesClient.cs b/Octokit/Clients/IRepositoriesClient.cs index 56a4b7540a..b8976f0706 100644 --- a/Octokit/Clients/IRepositoriesClient.cs +++ b/Octokit/Clients/IRepositoriesClient.cs @@ -9,7 +9,7 @@ namespace Octokit /// A client for GitHub's Repositories API. /// /// - /// See the Repositories API documentation for more details. + /// See the Repositories API documentation for more details. /// public interface IRepositoriesClient { @@ -17,10 +17,18 @@ public interface IRepositoriesClient /// Client for managing pull requests. /// /// - /// See the Pull Requests API documentation for more details + /// See the Pull Requests API documentation for more details /// IPullRequestsClient PullRequest { get; } + /// + /// Client for managing Actions in a repository. + /// + /// + /// See the Repository Actions API documentation for more information. + /// + IRepositoryActionsClient Actions { get; } + /// /// Client for managing branches in a repository. /// diff --git a/Octokit/Clients/IRepositoryActionsClient.cs b/Octokit/Clients/IRepositoryActionsClient.cs new file mode 100644 index 0000000000..af0aabe896 --- /dev/null +++ b/Octokit/Clients/IRepositoryActionsClient.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit +{ + /// + /// A client for GitHub's Repository Actions API. + /// + /// + /// See the Repository Actions API documentation for more details. + /// + public interface IRepositoryActionsClient + { + /// + /// Client for GitHub's Repository Actions API + /// + /// + /// See the Deployments API documentation for more details + /// + IRepositorySecretsClient Secrets { get; } + } +} diff --git a/Octokit/Clients/IRepositorySecretsClient.cs b/Octokit/Clients/IRepositorySecretsClient.cs new file mode 100644 index 0000000000..50213bec2a --- /dev/null +++ b/Octokit/Clients/IRepositorySecretsClient.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Repository Secrets API. + /// + /// + /// See the Repository Secrets API documentation for more details. + /// + public interface IRepositorySecretsClient + { + /// + /// Get the public signing key to encrypt secrets for a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown when a general API error occurs. + /// A instance for the repository public key. + Task GetPublicKey(string owner, string repoName); + + /// + /// List the secrets for a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown when a general API error occurs. + /// A instance for the list of repository secrets. + Task GetAll (string owner, string repoName); + + /// + /// Get a secret from a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// Thrown when a general API error occurs. + /// A instance for the repository secret. + Task Get(string owner, string repoName, string secretName); + + /// + /// Create or update a secret in a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// The encrypted value and id of the encryption key + /// Thrown when a general API error occurs. + /// A instance for the repository secret that was created or updated. + Task CreateOrUpdate(string owner, string repoName, string secretName, UpsertRepositorySecret upsertSecret); + + /// + /// Delete a secret in a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// Thrown when a general API error occurs. + Task Delete(string owner, string repoName, string secretName); + } +} diff --git a/Octokit/Clients/OrganizationActionsClient.cs b/Octokit/Clients/OrganizationActionsClient.cs new file mode 100644 index 0000000000..639c728a9b --- /dev/null +++ b/Octokit/Clients/OrganizationActionsClient.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit +{ + /// + /// A client for GitHub's Org Actions API. + /// + /// + /// See the Actions API documentation for more information. + /// + public class OrganizationActionsClient : ApiClient, IOrganizationActionsClient + { + /// + /// Initializes a new GitHub Orgs Actions API client. + /// + /// An API connection + public OrganizationActionsClient(IApiConnection apiConnection) : base(apiConnection) + { + Secrets = new OrganizationSecretsClient(apiConnection); + } + + /// + /// Returns a client to manage organization secrets. + /// + /// + /// See the Secrets API documentation for more information. + /// + public IOrganizationSecretsClient Secrets { get; private set; } + } +} diff --git a/Octokit/Clients/OrganizationSecretsClient.cs b/Octokit/Clients/OrganizationSecretsClient.cs new file mode 100644 index 0000000000..fb126eb915 --- /dev/null +++ b/Octokit/Clients/OrganizationSecretsClient.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Organization Secrets API. + /// + /// + /// See the Organization Secrets API documentation for more details. + /// + public class OrganizationSecretsClient : ApiClient, IOrganizationSecretsClient + { + public OrganizationSecretsClient(IApiConnection apiConnection) : base(apiConnection) { } + + /// + /// Get the public signing key to encrypt secrets for an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// Thrown when a general API error occurs. + /// A instance for the organization public key. + [ManualRoute("GET", "/orgs/{org}/actions/secrets/public-key")] + public Task GetPublicKey(string org) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + + return ApiConnection.Get(ApiUrls.OrganizationRepositorySecretPublicKey(org)); + } + + /// + /// List the secrets for an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// Thrown when a general API error occurs. + /// A instance for the list of organization secrets. + [ManualRoute("GET", "/orgs/{org}/actions/secrets")] + public Task GetAll(string org) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + + return ApiConnection.Get(ApiUrls.OrganizationRepositorySecrets(org)); + } + + /// + /// Get a secret from an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + /// A instance for the organization secret. + [ManualRoute("GET", "/orgs/{org}/actions/secrets/{secretName}")] + public Task Get(string org, string secretName) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + + return ApiConnection.Get(ApiUrls.OrganizationRepositorySecret(org, secretName)); + } + + /// + /// Create or update a secret in an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The encrypted value, id of the encryption key, and visibility info to upsert + /// Thrown when a general API error occurs. + /// A instance for the organization secret that was created or updated. + [ManualRoute("PUT", "/orgs/{org}/actions/secrets/{secretName}")] + public async Task CreateOrUpdate(string org, string secretName, UpsertOrganizationSecret upsertSecret) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + Ensure.ArgumentNotNull(upsertSecret, nameof(upsertSecret)); + Ensure.ArgumentNotNullOrEmptyString(upsertSecret.KeyId, nameof(upsertSecret.KeyId)); + Ensure.ArgumentNotNullOrEmptyString(upsertSecret.EncryptedValue, nameof(upsertSecret.EncryptedValue)); + Ensure.ArgumentNotNullOrEmptyString(upsertSecret.Visibility, nameof(upsertSecret.Visibility)); + + await ApiConnection.Put(ApiUrls.OrganizationRepositorySecret(org, secretName), upsertSecret); + return await ApiConnection.Get(ApiUrls.OrganizationRepositorySecret(org, secretName)); + } + + /// + /// Delete a secret in an organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + [ManualRoute("DELETE", "/orgs/{org}/actions/secrets/{secretName}")] + public Task Delete(string org, string secretName) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + + return ApiConnection.Delete(ApiUrls.OrganizationRepositorySecret(org, secretName)); + } + + /// + /// Get the list of selected sites that have access to a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// Thrown when a general API error occurs. + [ManualRoute("GET", "/orgs/{org}/actions/secrets/{secretName}/repositories")] + public Task GetSelectedRepositoriesForSecret(string org, string secretName) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + + return ApiConnection.Get(ApiUrls.OrganizationRepositorySecretRepositories(org, secretName)); + } + + /// + /// Set the list of selected sites that have access to a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The list of repositories that should have access to view and use the secret + /// Thrown when a general API error occurs. + [ManualRoute("PUT", "/orgs/{org}/actions/secrets/{secretName}/repositories")] + public async Task SetSelectedRepositoriesForSecret(string org, string secretName, SelectedRepositoryCollection repositories) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + Ensure.ArgumentNotNull(repositories, nameof(repositories)); + Ensure.ArgumentNotNull(repositories.SelectedRepositoryIds, nameof(repositories.SelectedRepositoryIds)); + + await ApiConnection.Put(ApiUrls.OrganizationRepositorySecretRepositories(org, secretName), repositories); + return; + } + + /// + /// Add a selected site to the visibility list of a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The id of the repo to add to the visibility list of the secret + /// Thrown when a general API error occurs. + [ManualRoute("PUT", "/orgs/{org}/actions/secrets/{secretName}/repositories/{repoId}")] + public Task AddRepoToOrganizationSecret(string org, string secretName, long repoId) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + Ensure.ArgumentNotNull(repoId, nameof(repoId)); + + return ApiConnection.Put(ApiUrls.OrganizationRepositorySecretRepository(org, secretName, repoId)); + } + + /// + /// ARemoved a selected site from the visibility list of a secret. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The name of the secret + /// The id of the repo to add to the visibility list of the secret + /// Thrown when a general API error occurs. + [ManualRoute("DELETE", "/orgs/{org}/actions/secrets/{secretName}/repositories/{repoId}")] + public Task RemoveRepoFromOrganizationSecret(string org, string secretName, long repoId) + { + Ensure.ArgumentNotNullOrEmptyString(org, nameof(org)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + Ensure.ArgumentNotNull(repoId, nameof(repoId)); + + return ApiConnection.Delete(ApiUrls.OrganizationRepositorySecretRepository(org, secretName, repoId)); + } + } +} diff --git a/Octokit/Clients/OrganizationsClient.cs b/Octokit/Clients/OrganizationsClient.cs index fb07b3a90e..57abb1e716 100644 --- a/Octokit/Clients/OrganizationsClient.cs +++ b/Octokit/Clients/OrganizationsClient.cs @@ -22,6 +22,7 @@ public OrganizationsClient(IApiConnection apiConnection) : base(apiConnection) Team = new TeamsClient(apiConnection); Hook = new OrganizationHooksClient(apiConnection); OutsideCollaborator = new OrganizationOutsideCollaboratorsClient(apiConnection); + Actions = new OrganizationActionsClient(apiConnection); } /// @@ -34,6 +35,11 @@ public OrganizationsClient(IApiConnection apiConnection) : base(apiConnection) /// public ITeamsClient Team { get; private set; } + /// + /// Returns a client to manage organization actions. + /// + public IOrganizationActionsClient Actions { get; private set; } + /// /// Returns a client to manage outside collaborators of an organization. /// diff --git a/Octokit/Clients/RepositoriesClient.cs b/Octokit/Clients/RepositoriesClient.cs index 9de3bc7035..9d208a7b6f 100644 --- a/Octokit/Clients/RepositoriesClient.cs +++ b/Octokit/Clients/RepositoriesClient.cs @@ -39,6 +39,7 @@ public RepositoriesClient(IApiConnection apiConnection) : base(apiConnection) Branch = new RepositoryBranchesClient(apiConnection); Traffic = new RepositoryTrafficClient(apiConnection); Project = new ProjectsClient(apiConnection); + Actions = new RepositoryActionsClient(apiConnection); } /// @@ -482,6 +483,14 @@ public Task> GetAllForOrg(string organization, ApiOpti return ApiConnection.GetAll(ApiUrls.OrganizationRepositories(organization), null, options); } + /// + /// Client for managing Actions in a repository. + /// + /// + /// See the Repository Actions API documentation for more information. + /// + public IRepositoryActionsClient Actions { get; private set; } + /// /// A client for GitHub's Repository Branches API. /// diff --git a/Octokit/Clients/RepositoryActionsClient.cs b/Octokit/Clients/RepositoryActionsClient.cs new file mode 100644 index 0000000000..9837448eba --- /dev/null +++ b/Octokit/Clients/RepositoryActionsClient.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Octokit +{ + /// + /// A client for GitHub's Repository Actions API. + /// + /// + /// See the Repository Actions API documentation for more details. + /// + public class RepositoryActionsClient : ApiClient, IRepositoryActionsClient + { + /// + /// Initializes a new GitHub Repository Actions API client. + /// + /// An API connection + public RepositoryActionsClient(IApiConnection apiConnection) : base(apiConnection) + { + Secrets = new RepositorySecretsClient(apiConnection); + } + + /// + /// Client for GitHub's Repository Actions API + /// + /// + /// See the Deployments API documentation for more details + /// + public IRepositorySecretsClient Secrets { get; set; } + } +} diff --git a/Octokit/Clients/RepositorySecretsClient.cs b/Octokit/Clients/RepositorySecretsClient.cs new file mode 100644 index 0000000000..7810955bd1 --- /dev/null +++ b/Octokit/Clients/RepositorySecretsClient.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Octokit +{ + public class RepositorySecretsClient : ApiClient, IRepositorySecretsClient + { + /// + /// Initializes a new GitHub Repository Branches API client. + /// + /// An API connection + public RepositorySecretsClient(IApiConnection apiConnection) : base(apiConnection) + { + } + + /// + /// Get the public signing key to encrypt secrets for a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown when a general API error occurs. + /// A instance for the repository public key. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/secrets/public-key")] + public Task GetPublicKey(string owner, string repoName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repoName, nameof(repoName)); + + var url = ApiUrls.RepositorySecretsPublicKey(owner, repoName); + + return ApiConnection.Get(url); + } + + /// + /// List the secrets for a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown when a general API error occurs. + /// A instance for the list of repository secrets. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/secrets")] + public Task GetAll(string owner, string repoName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repoName, nameof(repoName)); + + var url = ApiUrls.RepositorySecrets(owner, repoName); + + return ApiConnection.Get(url); + } + + /// + /// Get a secret from a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// Thrown when a general API error occurs. + /// A instance for the repository secret. + [ManualRoute("GET", "/repos/{owner}/{repo}/actions/secrets/{secretName}")] + public Task Get(string owner, string repoName, string secretName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repoName, nameof(repoName)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + + var url = ApiUrls.RepositorySecret(owner, repoName, secretName); + + return ApiConnection.Get(url); + } + + /// + /// Create or update a secret in a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// The encrypted value and id of the encryption key + /// Thrown when a general API error occurs. + /// A instance for the repository secret that was created or updated. + [ManualRoute("PUT", "/repos/{owner}/{repo}/actions/secrets/{secretName}")] + public async Task CreateOrUpdate(string owner, string repoName, string secretName, UpsertRepositorySecret upsertSecret) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repoName, nameof(repoName)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + Ensure.ArgumentNotNull(upsertSecret, nameof(upsertSecret)); + Ensure.ArgumentNotNullOrEmptyString(upsertSecret.EncryptedValue, nameof(upsertSecret.EncryptedValue)); + Ensure.ArgumentNotNullOrEmptyString(upsertSecret.KeyId, nameof(upsertSecret.KeyId)); + + var url = ApiUrls.RepositorySecret(owner, repoName, secretName); + + await ApiConnection.Put(url, upsertSecret); + + return await Get(owner, repoName, secretName); + } + + /// + /// Delete a secret in a repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// The name of the secret + /// Thrown when a general API error occurs. + [ManualRoute("DELETE", "/repos/{owner}/{repo}/actions/secrets/{secretName}")] + public Task Delete(string owner, string repoName, string secretName) + { + Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); + Ensure.ArgumentNotNullOrEmptyString(repoName, nameof(repoName)); + Ensure.ArgumentNotNullOrEmptyString(secretName, nameof(secretName)); + + var url = ApiUrls.RepositorySecret(owner, repoName, secretName); + + return ApiConnection.Delete(url); + } + } +} diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index e24de63a42..c6d57704ca 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -78,6 +78,64 @@ public static Uri OrganizationRepositories(string organization) return "orgs/{0}/repos".FormatUri(organization); } + /// + /// Returns the that returns all of the secrets for the specified organization in + /// response to a GET request. + /// + /// The name of the organization + /// + public static Uri OrganizationRepositorySecrets(string organization) + { + return "orgs/{0}/actions/secrets".FormatUri(organization); + } + + /// + /// Returns the that returns a secret for the specified organization in + /// response to a GET request. A POST to this URL creates a new secret for the organization. + /// + /// The name of the organization + /// The name of the secret + /// + public static Uri OrganizationRepositorySecret(string organization, string secret) + { + return "orgs/{0}/actions/secrets/{1}".FormatUri(organization,secret); + } + + /// + /// Returns the that returns the public key for signing secrets for the specified organization in + /// response to a GET request. + /// + /// The name of the organization + /// + public static Uri OrganizationRepositorySecretPublicKey(string organization) + { + return "orgs/{0}/actions/secrets/public-key".FormatUri(organization); + } + + /// + /// Returns the that returns a list of repositories for a secret for the specified organization in + /// response to a GET request. A POST to this URL sets the full repository list for a secret in the organization. + /// + /// The name of the organization + /// The name of the secret + /// + public static Uri OrganizationRepositorySecretRepositories(string organization, string secret) + { + return "orgs/{0}/actions/secrets/{1}/repositories".FormatUri(organization, secret); + } + + /// + /// Returns the that adds (PUT) or removes (DELETE) a repository from the visibility list of a secret. + /// + /// The name of the organization + /// The name of the secret + /// The id of the repo to target + /// + public static Uri OrganizationRepositorySecretRepository(string organization, string secret, long repoId) + { + return "orgs/{0}/actions/secrets/{1}/repositories/{2}".FormatUri(organization, secret, repoId.ToString()); + } + /// /// Returns the that returns all of the organizations for the currently logged in user. /// @@ -4387,6 +4445,40 @@ public static Uri CheckSuitePreferences(string owner, string repo) return "repos/{0}/{1}/check-suites/preferences".FormatUri(owner, repo); } + /// + /// Returns the that handles the repository secrets for the repository + /// + /// The owner of the repo + /// The name of the repo + /// The name of the secret + /// The that handles the repository secrets for the repository + public static Uri RepositorySecret(string owner, string repo, string secret) + { + return "repos/{0}/{1}/actions/secrets/{2}".FormatUri(owner, repo, secret); + } + + /// + /// Returns the that handles the repository secrets for the repository + /// + /// The owner of the repo + /// The name of the repo + /// The that handles the repository secrets for the repository + public static Uri RepositorySecrets(string owner, string repo) + { + return "repos/{0}/{1}/actions/secrets".FormatUri(owner, repo); + } + + /// + /// Returns the that handles the repository secrets for the repository + /// + /// The owner of the repo + /// The name of the repo + /// The that handles the repository secrets for the repository + public static Uri RepositorySecretsPublicKey(string owner, string repo) + { + return "repos/{0}/{1}/actions/secrets/public-key".FormatUri(owner, repo); + } + /// /// Returns the that returns all emojis in /// response to a GET request. diff --git a/Octokit/Models/Request/SelectedRepositoryCollection.cs b/Octokit/Models/Request/SelectedRepositoryCollection.cs new file mode 100644 index 0000000000..bf11200a5c --- /dev/null +++ b/Octokit/Models/Request/SelectedRepositoryCollection.cs @@ -0,0 +1,32 @@ +using Octokit.Internal; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; + +namespace Octokit +{ + /// + /// Represents request to set the repositories with visibility to the secrets in an organization. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class SelectedRepositoryCollection + { + public SelectedRepositoryCollection() + { + } + + public SelectedRepositoryCollection(IEnumerable selectedRepositoryIds) + { + SelectedRepositoryIds = selectedRepositoryIds; + } + + /// + /// List of repository Ids that should have visibility to the repository + /// + [Parameter(Key = "selected_repository_ids")] + public IEnumerable SelectedRepositoryIds { get; set; } + + internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "SelectedRepositoryCollection: Count: {0}", SelectedRepositoryIds.Count()); + } +} diff --git a/Octokit/Models/Request/UpsertOrganizationSecret.cs b/Octokit/Models/Request/UpsertOrganizationSecret.cs new file mode 100644 index 0000000000..bd8f78fa32 --- /dev/null +++ b/Octokit/Models/Request/UpsertOrganizationSecret.cs @@ -0,0 +1,38 @@ +using Octokit.Internal; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Represents request to change the value of a secret for an organization. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class UpsertOrganizationSecret : UpsertRepositorySecret + { + public UpsertOrganizationSecret() { } + + public UpsertOrganizationSecret(string encryptedValue, string encryptionKeyId, string visibility, IEnumerable selectedRepositoriesIds) + { + EncryptedValue = encryptedValue; + KeyId = encryptionKeyId; + Visibility = visibility; + SelectedRepositoriesIds = selectedRepositoriesIds; + } + + /// + /// The visibility level of the secret + /// + [Parameter(Key = "visibility")] + public string Visibility { get; set; } + + /// + /// The list of ids for the repositories with selected visibility to the secret + /// + [Parameter(Key = "selected_repository_ids")] + public IEnumerable SelectedRepositoriesIds { get; set; } + + internal new string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "UpsertOrganizationSecret: Key ID: {0}", KeyId); + } +} diff --git a/Octokit/Models/Request/UpsertRepositorySecret.cs b/Octokit/Models/Request/UpsertRepositorySecret.cs new file mode 100644 index 0000000000..8fe5ee3657 --- /dev/null +++ b/Octokit/Models/Request/UpsertRepositorySecret.cs @@ -0,0 +1,35 @@ +using Octokit.Internal; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Used to create or update a repository secret + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class UpsertRepositorySecret + { + /// + /// The encrypted value of the secret. + /// + /// See the API documentation for more information on how to encrypt the secret + [Parameter(Value = "encrypted_value")] + public string EncryptedValue { get; set; } + + /// + /// The id of the encryption key used to encrypt the secret. + /// + /// Get key and id from and use the API documentation for more information on how to encrypt the secret + [Parameter(Value = "key_id")] + public string KeyId { get; set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "KeyId: {0}", KeyId); + } + } + } +} diff --git a/Octokit/Models/Response/OrganizationSecret.cs b/Octokit/Models/Response/OrganizationSecret.cs new file mode 100644 index 0000000000..18f0cb0755 --- /dev/null +++ b/Octokit/Models/Response/OrganizationSecret.cs @@ -0,0 +1,39 @@ +using Octokit.Internal; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text; + +namespace Octokit +{ + /// + /// Represents an organization secret. + /// Does not contain the secret value + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class OrganizationSecret : RepositorySecret + { + public OrganizationSecret() { } + + public OrganizationSecret(string name, DateTime createdAt, DateTime updatedAt, string visibility, string selectedRepositoriesUrl) + { + Name = name; + CreatedAt = createdAt; + UpdatedAt = updatedAt; + Visibility = visibility; + SelectedRepositoriesUrl = selectedRepositoriesUrl; + } + + /// + /// The visibility level of the secret within the organization + /// + public string Visibility { get; private set; } + + /// + /// The URL to retrieve the list of selected repositories + /// + [Parameter(Key = "selected_repositories_url")] + public string SelectedRepositoriesUrl { get; private set; } + } +} diff --git a/Octokit/Models/Response/OrganizationSecretRepositoryCollection.cs b/Octokit/Models/Response/OrganizationSecretRepositoryCollection.cs new file mode 100644 index 0000000000..e7914c7eac --- /dev/null +++ b/Octokit/Models/Response/OrganizationSecretRepositoryCollection.cs @@ -0,0 +1,38 @@ +using Octokit.Internal; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Represents response of the repositories for a secret in an organization. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class OrganizationSecretRepositoryCollection + { + public OrganizationSecretRepositoryCollection() + { + } + + public OrganizationSecretRepositoryCollection(int count, IReadOnlyList repositories) + { + Count = count; + Repositories = repositories; + } + + /// + /// The total count of repositories with visibility to the secret in the organization + /// + [Parameter(Key = "total_count")] + public int Count { get; private set; } + + /// + /// The list of repositories with visibility to the secret in the organization + /// + [Parameter(Key = "repositories")] + public IReadOnlyList Repositories { get; private set; } + + internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "OrganizationSecretRepositoryCollection: Count: {0}", Count); + } +} diff --git a/Octokit/Models/Response/OrganizationSecretsCollection.cs b/Octokit/Models/Response/OrganizationSecretsCollection.cs new file mode 100644 index 0000000000..f957de1a27 --- /dev/null +++ b/Octokit/Models/Response/OrganizationSecretsCollection.cs @@ -0,0 +1,38 @@ +using Octokit.Internal; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Represents response of secrets for an organization. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class OrganizationSecretsCollection + { + public OrganizationSecretsCollection() + { + } + + public OrganizationSecretsCollection(int count, IReadOnlyList secrets) + { + Count = count; + Secrets = secrets; + } + + /// + /// The total count of secrets for the organization + /// + [Parameter(Key = "total_count")] + public int Count { get; private set; } + + /// + /// The list of secrets for the organization + /// + [Parameter(Key = "secrets")] + public IReadOnlyList Secrets { get; private set; } + + internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "OrganizationSecretCollection: Count: {0}", Count); + } +} diff --git a/Octokit/Models/Response/RepositorySecret.cs b/Octokit/Models/Response/RepositorySecret.cs new file mode 100644 index 0000000000..f2559a2311 --- /dev/null +++ b/Octokit/Models/Response/RepositorySecret.cs @@ -0,0 +1,50 @@ +using Octokit.Internal; +using System; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Represents a repository secret. + /// Does not contain the secret value + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RepositorySecret + { + public RepositorySecret() + { + } + + public RepositorySecret(string name, DateTimeOffset createdAt, DateTimeOffset? updatedAt) + { + Name = name; + CreatedAt = createdAt; + UpdatedAt = updatedAt; + } + + /// + /// The name of the repository secret + /// + public string Name { get; protected set; } + + /// + /// The date and time that the secret was created + /// + public DateTimeOffset CreatedAt { get; protected set; } + + /// + /// The date and time the secret was last updated + /// + public DateTimeOffset? UpdatedAt { get; protected set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, + "RepositorySecret: Name: {0}", Name); + } + } + } +} diff --git a/Octokit/Models/Response/RepositorySecretsCollection.cs b/Octokit/Models/Response/RepositorySecretsCollection.cs new file mode 100644 index 0000000000..5f900bf6f4 --- /dev/null +++ b/Octokit/Models/Response/RepositorySecretsCollection.cs @@ -0,0 +1,36 @@ +using Octokit.Internal; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Represents response of secrets for a repository. + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RepositorySecretsCollection + { + public RepositorySecretsCollection() + { + } + + public RepositorySecretsCollection(int totalCount, IReadOnlyList secrets) + { + TotalCount = totalCount; + Secrets = secrets; + } + + /// + /// The total count of secrets for the repository + /// + public int TotalCount { get; private set; } + + /// + /// The list of secrets for the repository + /// + public IReadOnlyList Secrets { get; private set; } + + internal string DebuggerDisplay => string.Format(CultureInfo.CurrentCulture, "RepositorySecretsCollection: Count: {0}", TotalCount); + } +} diff --git a/Octokit/Models/Response/SecretsPublicKey.cs b/Octokit/Models/Response/SecretsPublicKey.cs new file mode 100644 index 0000000000..b7b7fda0a7 --- /dev/null +++ b/Octokit/Models/Response/SecretsPublicKey.cs @@ -0,0 +1,41 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Represents the public key used to sign the secrets to post to GitHub + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class SecretsPublicKey + { + public SecretsPublicKey() + { + } + + public SecretsPublicKey(string keyId, string key) + { + KeyId = keyId; + Key = key; + } + + /// + /// The id of this repository public key. Needed to create or update a secret + /// + public string KeyId { get; private set; } + + /// + /// The public key for this repository + /// + public string Key { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, + "RepositorySecretPublicKey: Id: {0}", KeyId); + } + } + } +}