From 329ef960edcc5d553d5a830fcfa43a8fe128463a Mon Sep 17 00:00:00 2001 From: Martin Scholz Date: Sun, 23 Jul 2017 00:18:34 +0200 Subject: [PATCH] [WIP] Add new Project API Preview (#1480) Add Projects API client (preview) --- .../Clients/IObservableProjectCardsClient.cs | 82 ++++ .../IObservableProjectColumnsClient.cs | 82 ++++ .../Clients/IObservableProjectsClient.cs | 203 +++++++++ .../Clients/IObservableRepositoriesClient.cs | 8 + .../Clients/ObservableProjectCardsClient.cs | 125 ++++++ .../Clients/ObservableProjectColumnsClient.cs | 119 ++++++ .../Clients/ObservableProjectsClient.cs | 310 ++++++++++++++ .../Clients/ObservableRepositoriesClient.cs | 9 + .../Clients/ProjectCardsClientTests.cs | 354 +++++++++++++++ .../Clients/ProjectColumnsClientTests.cs | 293 +++++++++++++ .../Clients/ProjectsClientTests.cs | 402 +++++++++++++++++ .../ObservableProjectCardsClientTests.cs | 356 +++++++++++++++ .../ObservableProjectColumnsClientTests.cs | 295 +++++++++++++ .../Reactive/ObservableProjectsClientTests.cs | 404 ++++++++++++++++++ .../Clients/DeploymentsClientTests.cs | 2 +- .../Clients/ProjectCardsClientTests.cs | 159 +++++++ .../Clients/ProjectColumnsClientTests.cs | 160 +++++++ Octokit.Tests/Clients/ProjectsClientTests.cs | 271 ++++++++++++ .../ObservableProjectCardsClientTests.cs | 150 +++++++ .../ObservableProjectColumnsClientTests.cs | 150 +++++++ .../Reactive/ObservableProjectsClientTests.cs | 279 ++++++++++++ Octokit/Clients/IProjectCardsClient.cs | 84 ++++ Octokit/Clients/IProjectColumnsClient.cs | 83 ++++ Octokit/Clients/IProjectsClient.cs | 204 +++++++++ Octokit/Clients/IRepositoriesClient.cs | 8 + Octokit/Clients/ProjectCardsClient.cs | 135 ++++++ Octokit/Clients/ProjectColumnsClient.cs | 134 ++++++ Octokit/Clients/ProjectsClient.cs | 303 +++++++++++++ Octokit/Clients/RepositoriesClient.cs | 9 + Octokit/Helpers/AcceptHeaders.cs | 2 + Octokit/Helpers/ApiUrls.cs | 101 +++++ Octokit/Http/Connection.cs | 8 + Octokit/Http/IConnection.cs | 11 +- Octokit/Models/Request/NewProject.cs | 36 ++ Octokit/Models/Request/NewProjectCard.cs | 52 +++ Octokit/Models/Request/NewProjectColumn.cs | 27 ++ Octokit/Models/Request/ProjectCardMove.cs | 46 ++ Octokit/Models/Request/ProjectCardUpdate.cs | 27 ++ Octokit/Models/Request/ProjectColumnMove.cs | 43 ++ Octokit/Models/Request/ProjectColumnUpdate.cs | 27 ++ Octokit/Models/Request/ProjectRequest.cs | 30 ++ Octokit/Models/Request/ProjectUpdate.cs | 36 ++ Octokit/Models/Response/Project.cs | 84 ++++ Octokit/Models/Response/ProjectCard.cs | 66 +++ Octokit/Models/Response/ProjectColumn.cs | 54 +++ 45 files changed, 5821 insertions(+), 2 deletions(-) create mode 100644 Octokit.Reactive/Clients/IObservableProjectCardsClient.cs create mode 100644 Octokit.Reactive/Clients/IObservableProjectColumnsClient.cs create mode 100644 Octokit.Reactive/Clients/IObservableProjectsClient.cs create mode 100644 Octokit.Reactive/Clients/ObservableProjectCardsClient.cs create mode 100644 Octokit.Reactive/Clients/ObservableProjectColumnsClient.cs create mode 100644 Octokit.Reactive/Clients/ObservableProjectsClient.cs create mode 100644 Octokit.Tests.Integration/Clients/ProjectCardsClientTests.cs create mode 100644 Octokit.Tests.Integration/Clients/ProjectColumnsClientTests.cs create mode 100644 Octokit.Tests.Integration/Clients/ProjectsClientTests.cs create mode 100644 Octokit.Tests.Integration/Reactive/ObservableProjectCardsClientTests.cs create mode 100644 Octokit.Tests.Integration/Reactive/ObservableProjectColumnsClientTests.cs create mode 100644 Octokit.Tests.Integration/Reactive/ObservableProjectsClientTests.cs create mode 100644 Octokit.Tests/Clients/ProjectCardsClientTests.cs create mode 100644 Octokit.Tests/Clients/ProjectColumnsClientTests.cs create mode 100644 Octokit.Tests/Clients/ProjectsClientTests.cs create mode 100644 Octokit.Tests/Reactive/ObservableProjectCardsClientTests.cs create mode 100644 Octokit.Tests/Reactive/ObservableProjectColumnsClientTests.cs create mode 100644 Octokit.Tests/Reactive/ObservableProjectsClientTests.cs create mode 100644 Octokit/Clients/IProjectCardsClient.cs create mode 100644 Octokit/Clients/IProjectColumnsClient.cs create mode 100644 Octokit/Clients/IProjectsClient.cs create mode 100644 Octokit/Clients/ProjectCardsClient.cs create mode 100644 Octokit/Clients/ProjectColumnsClient.cs create mode 100644 Octokit/Clients/ProjectsClient.cs create mode 100644 Octokit/Models/Request/NewProject.cs create mode 100644 Octokit/Models/Request/NewProjectCard.cs create mode 100644 Octokit/Models/Request/NewProjectColumn.cs create mode 100644 Octokit/Models/Request/ProjectCardMove.cs create mode 100644 Octokit/Models/Request/ProjectCardUpdate.cs create mode 100644 Octokit/Models/Request/ProjectColumnMove.cs create mode 100644 Octokit/Models/Request/ProjectColumnUpdate.cs create mode 100644 Octokit/Models/Request/ProjectRequest.cs create mode 100644 Octokit/Models/Request/ProjectUpdate.cs create mode 100644 Octokit/Models/Response/Project.cs create mode 100644 Octokit/Models/Response/ProjectCard.cs create mode 100644 Octokit/Models/Response/ProjectColumn.cs diff --git a/Octokit.Reactive/Clients/IObservableProjectCardsClient.cs b/Octokit.Reactive/Clients/IObservableProjectCardsClient.cs new file mode 100644 index 00000000..d9e8496f --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableProjectCardsClient.cs @@ -0,0 +1,82 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Project Cards API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public interface IObservableProjectCardsClient + { + /// + /// Gets all cards. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + IObservable GetAll(int columnId); + + /// + /// Gets all cards. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// Options for changing the API response + IObservable GetAll(int columnId, ApiOptions options); + + /// + /// Gets a single card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] + IObservable Get(int id); + + /// + /// Creates a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// The card to create + IObservable Create(int columnId, NewProjectCard newProjectCard); + + /// + /// Updates a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + /// New values to update the card with + IObservable Update(int id, ProjectCardUpdate projectCardUpdate); + + /// + /// Deletes a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + IObservable Delete(int id); + + /// + /// Moves a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + /// The position to move the card + IObservable Move(int id, ProjectCardMove position); + } +} diff --git a/Octokit.Reactive/Clients/IObservableProjectColumnsClient.cs b/Octokit.Reactive/Clients/IObservableProjectColumnsClient.cs new file mode 100644 index 00000000..b1eb1f50 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableProjectColumnsClient.cs @@ -0,0 +1,82 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Project Columns API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public interface IObservableProjectColumnsClient + { + /// + /// Gets all columns. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + IObservable GetAll(int projectId); + + /// + /// Gets all columns. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// Options for changing the API response + IObservable GetAll(int projectId, ApiOptions options); + + /// + /// Gets a single column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] + IObservable Get(int id); + + /// + /// Creates a column. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// The column to create + IObservable Create(int projectId, NewProjectColumn newProjectColumn); + + /// + /// Updates a column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// New values to update the column with + IObservable Update(int id, ProjectColumnUpdate projectColumnUpdate); + + /// + /// Deletes a column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + IObservable Delete(int id); + + /// + /// Moves a column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// The position to move the column + IObservable Move(int id, ProjectColumnMove position); + } +} diff --git a/Octokit.Reactive/Clients/IObservableProjectsClient.cs b/Octokit.Reactive/Clients/IObservableProjectsClient.cs new file mode 100644 index 00000000..dea4dca8 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableProjectsClient.cs @@ -0,0 +1,203 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Repository Projects API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public interface IObservableProjectsClient + { + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + IObservable GetAllForRepository(string owner, string name); + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter the list of projects returned + IObservable GetAllForRepository(string owner, string name, ProjectRequest request); + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + IObservable GetAllForRepository(long repositoryId); + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Used to filter the list of projects returned + IObservable GetAllForRepository(long repositoryId, ProjectRequest request); + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + IObservable GetAllForRepository(string owner, string name, ApiOptions options); + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter the list of projects returned + /// Options for changing the API response + IObservable GetAllForRepository(string owner, string name, ProjectRequest request, ApiOptions options); + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Options for changing the API response + IObservable GetAllForRepository(long repositoryId, ApiOptions options); + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Used to filter the list of projects returned + /// Options for changing the API response + IObservable GetAllForRepository(long repositoryId, ProjectRequest request, ApiOptions options); + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + IObservable GetAllForOrganization(string organization); + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Used to filter the list of projects returned + IObservable GetAllForOrganization(string organization, ProjectRequest request); + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Options for changing the API response + IObservable GetAllForOrganization(string organization, ApiOptions options); + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Used to filter the list of projects returned + /// Options for changing the API response + IObservable GetAllForOrganization(string organization, ProjectRequest request, ApiOptions options); + + /// + /// Gets a single project for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] + IObservable Get(int id); + + /// + /// Creates a project for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// The new project to create for the specified repository + IObservable CreateForRepository(long repositoryId, NewProject newProject); + + /// + /// Creates a project for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The new project to create for the specified repository + IObservable CreateForOrganization(string organization, NewProject newProject); + + /// + /// Updates a project for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// The modified project + IObservable Update(int id, ProjectUpdate projectUpdate); + + /// + /// Deletes a project. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + IObservable Delete(int id); + + /// + /// A client for GitHub's Project Cards API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + IObservableProjectCardsClient Card { get; } + + /// + /// A client for GitHub's Project Columns API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + IObservableProjectColumnsClient Column { get; } + } +} diff --git a/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs b/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs index 6685a426..dae1ec26 100644 --- a/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs +++ b/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs @@ -502,5 +502,13 @@ namespace Octokit.Reactive /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/traffic/ /// IObservableRepositoryTrafficClient Traffic { get; } + + /// + /// Access GitHub's Repository Projects API + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/projects/ + /// + IObservableProjectsClient Project { get; } } } diff --git a/Octokit.Reactive/Clients/ObservableProjectCardsClient.cs b/Octokit.Reactive/Clients/ObservableProjectCardsClient.cs new file mode 100644 index 00000000..cb5f0cde --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableProjectCardsClient.cs @@ -0,0 +1,125 @@ +using Octokit.Reactive.Internal; +using System; +using System.Reactive.Threading.Tasks; +using System.Collections.Generic; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Project Cards API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public class ObservableProjectCardsClient : IObservableProjectCardsClient + { + readonly IProjectCardsClient _client; + readonly IConnection _connection; + + public ObservableProjectCardsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.Repository.Project.Card; + _connection = client.Connection; + } + + /// + /// Gets all cards for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + public IObservable GetAll(int columnId) + { + return GetAll(columnId, ApiOptions.None); + } + + /// + /// Gets all cards for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// Options for changing the API response + public IObservable GetAll(int columnId, ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + var url = ApiUrls.ProjectCards(columnId); + + return _connection.GetAndFlattenAllPages(url, new Dictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Gets a single card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + public IObservable Get(int id) + { + return _client.Get(id).ToObservable(); + } + + /// + /// Creates a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// The card to create + public IObservable Create(int columnId, NewProjectCard newProjectCard) + { + Ensure.ArgumentNotNull(newProjectCard, "newProjectCard"); + + return _client.Create(columnId, newProjectCard).ToObservable(); + } + + /// + /// Updates a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + /// New values to update the card with + public IObservable Update(int id, ProjectCardUpdate projectCardUpdate) + { + Ensure.ArgumentNotNull(projectCardUpdate, "projectCardUpdate"); + + return _client.Update(id, projectCardUpdate).ToObservable(); + } + + /// + /// Deletes a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + public IObservable Delete(int id) + { + return _client.Delete(id).ToObservable(); + } + + /// + /// Moves a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + /// The position to move the card + public IObservable Move(int id, ProjectCardMove position) + { + Ensure.ArgumentNotNull(position, "position"); + + return _client.Move(id, position).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableProjectColumnsClient.cs b/Octokit.Reactive/Clients/ObservableProjectColumnsClient.cs new file mode 100644 index 00000000..50e6aba5 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableProjectColumnsClient.cs @@ -0,0 +1,119 @@ +using Octokit.Reactive.Internal; +using System; +using System.Reactive.Threading.Tasks; +using System.Collections.Generic; + +namespace Octokit.Reactive +{ + public class ObservableProjectColumnsClient : IObservableProjectColumnsClient + { + readonly IProjectColumnsClient _client; + readonly IConnection _connection; + + public ObservableProjectColumnsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.Repository.Project.Column; + _connection = client.Connection; + } + + /// + /// Gets all columns for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + public IObservable GetAll(int projectId) + { + return GetAll(projectId, ApiOptions.None); + } + + /// + /// Gets all columns for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// Options for changing the API response + public IObservable GetAll(int projectId, ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + var url = ApiUrls.ProjectColumns(projectId); + + return _connection.GetAndFlattenAllPages(url, new Dictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Gets a single column for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + public IObservable Get(int id) + { + return _client.Get(id).ToObservable(); + } + + /// + /// Creates a column for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the project + /// The column to create + public IObservable Create(int projectId, NewProjectColumn newProjectColumn) + { + Ensure.ArgumentNotNull(newProjectColumn, "newProjectColumn"); + + return _client.Create(projectId, newProjectColumn).ToObservable(); + } + + /// + /// Updates a column for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// New values to update the column with + public IObservable Update(int id, ProjectColumnUpdate projectColumnUpdate) + { + Ensure.ArgumentNotNull(projectColumnUpdate, "projectColumnUpdate"); + + return _client.Update(id, projectColumnUpdate).ToObservable(); + } + + /// + /// Deletes a column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + public IObservable Delete(int id) + { + return _client.Delete(id).ToObservable(); + } + + /// + /// Moves a column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// The position to move the column + public IObservable Move(int id, ProjectColumnMove position) + { + Ensure.ArgumentNotNull(position, "position"); + + return _client.Move(id, position).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableProjectsClient.cs b/Octokit.Reactive/Clients/ObservableProjectsClient.cs new file mode 100644 index 00000000..608c7f70 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableProjectsClient.cs @@ -0,0 +1,310 @@ +using Octokit.Reactive.Internal; +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reactive.Threading.Tasks; +using System.Collections.Generic; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Repository Projects API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public class ObservableProjectsClient : IObservableProjectsClient + { + readonly IProjectsClient _client; + readonly IConnection _connection; + + public ObservableProjectsClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.Repository.Project; + _connection = client.Connection; + Card = new ObservableProjectCardsClient(client); + Column = new ObservableProjectColumnsClient(client); + } + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + public IObservable GetAllForRepository(string owner, string name) + { + return GetAllForRepository(owner, name, ApiOptions.None); + } + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + public IObservable GetAllForRepository(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + var url = ApiUrls.RepositoryProjects(owner, name); + + return _connection.GetAndFlattenAllPages(url, new Dictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter the list of projects returned + public IObservable GetAllForRepository(string owner, string name, ProjectRequest request) + { + return GetAllForRepository(owner, name, request, ApiOptions.None); + } + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter the list of projects returned + /// Options for changing the API response + public IObservable GetAllForRepository(string owner, string name, ProjectRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + var url = ApiUrls.RepositoryProjects(owner, name); + + return _connection.GetAndFlattenAllPages(url, request.ToParametersDictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + public IObservable GetAllForRepository(long repositoryId) + { + return GetAllForRepository(repositoryId, ApiOptions.None); + } + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + public IObservable GetAllForRepository(long repositoryId, ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + var url = ApiUrls.RepositoryProjects(repositoryId); + + return _connection.GetAndFlattenAllPages(url, new Dictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Used to filter the list of projects returned + public IObservable GetAllForRepository(long repositoryId, ProjectRequest request) + { + return GetAllForRepository(repositoryId, request, ApiOptions.None); + } + + /// + /// Get all projects for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Used to filter the list of projects returned + /// Options for changing the API response + public IObservable GetAllForRepository(long repositoryId, ProjectRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + var url = ApiUrls.RepositoryProjects(repositoryId); + + return _connection.GetAndFlattenAllPages(url, request.ToParametersDictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + public IObservable GetAllForOrganization(string organization) + { + return GetAllForOrganization(organization, ApiOptions.None); + } + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Options for changing the API response + public IObservable GetAllForOrganization(string organization, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(options, "options"); + + var url = ApiUrls.OrganizationProjects(organization); + + return _connection.GetAndFlattenAllPages(url, new Dictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Used to filter the list of projects returned + public IObservable GetAllForOrganization(string organization, ProjectRequest request) + { + return GetAllForOrganization(organization, request, ApiOptions.None); + } + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Used to filter the list of projects returned + /// Options for changing the API response + public IObservable GetAllForOrganization(string organization, ProjectRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + var url = ApiUrls.OrganizationProjects(organization); + + return _connection.GetAndFlattenAllPages(url, request.ToParametersDictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Gets a single project for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] + public IObservable Get(int id) + { + return _client.Get(id).ToObservable(); + } + + /// + /// Creates a project for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// The new project to create for the specified repository + public IObservable CreateForRepository(long repositoryId, NewProject newProject) + { + Ensure.ArgumentNotNull(newProject, "newProject"); + + return _client.CreateForRepository(repositoryId, newProject).ToObservable(); + } + + /// + /// Creates a project for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The new project to create for the specified repository + public IObservable CreateForOrganization(string organization, NewProject newProject) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(newProject, "newProject"); + + return _client.CreateForOrganization(organization, newProject).ToObservable(); + } + + /// + /// Updates a project for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// The modified project + public IObservable Update(int id, ProjectUpdate projectUpdate) + { + Ensure.ArgumentNotNull(projectUpdate, "projectUpdate"); + + return _client.Update(id, projectUpdate).ToObservable(); + } + + /// + /// Deletes a project. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + public IObservable Delete(int id) + { + return _client.Delete(id).ToObservable(); + } + + /// + /// A client for GitHub's Project Cards API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public IObservableProjectCardsClient Card { get; private set; } + + /// + /// A client for GitHub's Project Columns API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public IObservableProjectColumnsClient Column { get; private set; } + } +} diff --git a/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs b/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs index 54b07b63..aea66d3b 100644 --- a/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs +++ b/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs @@ -37,6 +37,7 @@ namespace Octokit.Reactive Page = new ObservableRepositoryPagesClient(client); Invitation = new ObservableRepositoryInvitationsClient(client); Traffic = new ObservableRepositoryTrafficClient(client); + Project = new ObservableProjectsClient(client); } /// @@ -745,5 +746,13 @@ namespace Octokit.Reactive /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/traffic/ /// public IObservableRepositoryTrafficClient Traffic { get; private set; } + + /// + /// Access GitHub's Repository Projects API + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/projects/ + /// + public IObservableProjectsClient Project { get; private set; } } } diff --git a/Octokit.Tests.Integration/Clients/ProjectCardsClientTests.cs b/Octokit.Tests.Integration/Clients/ProjectCardsClientTests.cs new file mode 100644 index 00000000..53887276 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/ProjectCardsClientTests.cs @@ -0,0 +1,354 @@ +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Xunit; + +public class ProjectCardsClientTests +{ + public class TheGetAllMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheGetAllMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsAllCards() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column.Id); + var card2 = await CreateCardHelper(_github, column.Id); + + var result = await _github.Repository.Project.Card.GetAll(column.Id); + + Assert.Equal(2, result.Count); + Assert.True(result.FirstOrDefault(x => x.Id == card1.Id).Id == card1.Id); + Assert.True(result.FirstOrDefault(x => x.Id == card2.Id).Id == card2.Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfCardWithoutStart() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column.Id); + var card2 = await CreateCardHelper(_github, column.Id); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var cards = await _github.Repository.Project.Card.GetAll(column.Id, options); + + // NOTE: cards are returned in reverse order + Assert.Equal(1, cards.Count); + Assert.Equal(card2.Id, cards[0].Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfCardsWithStart() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column.Id); + var card2 = await CreateCardHelper(_github, column.Id); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var cards = await _github.Repository.Project.Card.GetAll(column.Id, options); + + // NOTE: cards are returned in reverse order + Assert.Equal(1, cards.Count); + Assert.Equal(card1.Id, cards[0].Id); + } + + [IntegrationTest] + public async Task ReturnsDistinctCardsBasedOnStartPage() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column.Id); + var card2 = await CreateCardHelper(_github, column.Id); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _github.Repository.Project.Card.GetAll(column.Id, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.Repository.Project.Card.GetAll(column.Id, skipStartOptions); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheGetMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheGetMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsCard() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card = await CreateCardHelper(_github, column.Id); + + var result = await _github.Repository.Project.Card.Get(card.Id); + + Assert.Equal(card.Id, result.Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheCreateMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheCreateMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task CreatesCard() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card = await CreateCardHelper(_github, column.Id); + + Assert.NotNull(card); + } + + [IntegrationTest] + public async Task CreatesIssueCard() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var issue = await _github.Issue.Create(_context.RepositoryId, new NewIssue("a test issue")); + var column = await CreateColumnHelper(_github, project.Id); + var card = await CreateIssueCardHelper(_github, issue.Id, column.Id); + + Assert.NotNull(card); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheUpdateMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheUpdateMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task UpdatesCard() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card = await CreateCardHelper(_github, column.Id); + var cardUpdate = new ProjectCardUpdate("newNameOfNote"); + + var result = await _github.Repository.Project.Card.Update(card.Id, cardUpdate); + + Assert.Equal("newNameOfNote", result.Note); + Assert.Equal(card.Id, result.Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheDeleteMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheDeleteMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task DeletesCard() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card = await CreateCardHelper(_github, column.Id); + + var result = await _github.Repository.Project.Card.Delete(card.Id); + + Assert.True(result); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheMoveMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheMoveMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task MovesCardWithinColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column.Id); + var card2 = await CreateCardHelper(_github, column.Id); + var card3 = await CreateCardHelper(_github, column.Id); + + var positionTop = new ProjectCardMove(ProjectCardPosition.Top, column.Id, null); + var positionBottom = new ProjectCardMove(ProjectCardPosition.Top, column.Id, null); + var positionAfter = new ProjectCardMove(ProjectCardPosition.Top, column.Id, card1.Id); + + var resultTop = await _github.Repository.Project.Card.Move(card2.Id, positionTop); + var resultBottom = await _github.Repository.Project.Card.Move(card2.Id, positionBottom); + var resultAfter = await _github.Repository.Project.Card.Move(card2.Id, positionTop); + + Assert.True(resultTop); + Assert.True(resultBottom); + Assert.True(resultAfter); + } + + [IntegrationTest] + public async Task MovesCardBetweenColumns() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column1.Id); + var card2 = await CreateCardHelper(_github, column1.Id); + var card3 = await CreateCardHelper(_github, column1.Id); + + var positionTop = new ProjectCardMove(ProjectCardPosition.Top, column2.Id, null); + var positionBottom = new ProjectCardMove(ProjectCardPosition.Top, column2.Id, null); + var positionAfter = new ProjectCardMove(ProjectCardPosition.Top, column2.Id, card1.Id); + + var resultTop = await _github.Repository.Project.Card.Move(card1.Id, positionTop); + var resultBottom = await _github.Repository.Project.Card.Move(card2.Id, positionBottom); + var resultAfter = await _github.Repository.Project.Card.Move(card3.Id, positionTop); + + Assert.True(resultTop); + Assert.True(resultBottom); + Assert.True(resultAfter); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + private static async Task CreateRepositoryProjectHelper(IGitHubClient githubClient, long repositoryId) + { + var newProject = new NewProject(Helper.MakeNameWithTimestamp("new-project")); + var result = await githubClient.Repository.Project.CreateForRepository(repositoryId, newProject); + + return result; + } + + private static async Task CreateColumnHelper(IGitHubClient githubClient, int projectId) + { + var newColumn = new NewProjectColumn(Helper.MakeNameWithTimestamp("new-project-column")); + var result = await githubClient.Repository.Project.Column.Create(projectId, newColumn); + + return result; + } + + private static async Task CreateCardHelper(IGitHubClient githubClient, int columnId) + { + var newCard = new NewProjectCard(Helper.MakeNameWithTimestamp("new-card")); + var result = await githubClient.Repository.Project.Card.Create(columnId, newCard); + + return result; + } + + private static async Task CreateIssueCardHelper(IGitHubClient githubClient, int issueId, int columnId) + { + var newCard = new NewProjectCard(issueId, ProjectCardContentType.Issue); + var result = await githubClient.Repository.Project.Card.Create(columnId, newCard); + + return result; + } +} + diff --git a/Octokit.Tests.Integration/Clients/ProjectColumnsClientTests.cs b/Octokit.Tests.Integration/Clients/ProjectColumnsClientTests.cs new file mode 100644 index 00000000..a8bfe67d --- /dev/null +++ b/Octokit.Tests.Integration/Clients/ProjectColumnsClientTests.cs @@ -0,0 +1,293 @@ +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Xunit; + +public class ProjectColumnsClientTests +{ + public class TheGetAllColumnsMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheGetAllColumnsMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsAllColumns() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + + var result = await _github.Repository.Project.Column.GetAll(project.Id); + + Assert.Equal(2, result.Count); + Assert.True(result.FirstOrDefault(x => x.Id == column1.Id).Id == column1.Id); + Assert.True(result.FirstOrDefault(x => x.Id == column2.Id).Id == column2.Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfColumnsWithoutStart() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var columns = await _github.Repository.Project.Column.GetAll(project.Id, options); + + Assert.Equal(1, columns.Count); + Assert.Equal(column1.Id, columns[0].Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfColumnsWithStart() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var columns = await _github.Repository.Project.Column.GetAll(project.Id, options); + + Assert.Equal(1, columns.Count); + Assert.Equal(column2.Id, columns[0].Id); + } + + [IntegrationTest] + public async Task ReturnsDistinctColumnsBasedOnStartPage() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _github.Repository.Project.Column.GetAll(project.Id, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.Repository.Project.Column.GetAll(project.Id, skipStartOptions); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheGetColumnMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheGetColumnMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + + var result = await _github.Repository.Project.Column.Get(column.Id); + + Assert.Equal(column.Id, result.Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheCreateColumnMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheCreateColumnMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task CreatesColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + + Assert.Equal(project.Url, column.ProjectUrl); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheUpdateColumnMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheUpdateColumnMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task UpdatesColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + + var columnUpdate = new ProjectColumnUpdate("newName"); + + var result = await _github.Repository.Project.Column.Update(column.Id, columnUpdate); + + Assert.Equal("newName", result.Name); + Assert.Equal(column.Id, result.Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheDeleteColumnMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheDeleteColumnMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task DeletesColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + + var result = await _github.Repository.Project.Column.Delete(column.Id); + + Assert.True(result); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheMoveColumnMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheMoveColumnMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task MovesColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + + var positionFirst = new ProjectColumnMove(ProjectColumnPosition.First, null); + var positionLast = new ProjectColumnMove(ProjectColumnPosition.Last, null); + var positionAfter = new ProjectColumnMove(ProjectColumnPosition.After, column1.Id); + + var resultFirst = await _github.Repository.Project.Column.Move(column2.Id, positionFirst); + var resultLast = await _github.Repository.Project.Column.Move(column2.Id, positionLast); + var resultAfter = await _github.Repository.Project.Column.Move(column2.Id, positionAfter); + + Assert.True(resultFirst); + Assert.True(resultLast); + Assert.True(resultAfter); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + private static async Task CreateRepositoryProjectHelper(IGitHubClient githubClient, long repositoryId) + { + var newProject = new NewProject(Helper.MakeNameWithTimestamp("new-project")); + var result = await githubClient.Repository.Project.CreateForRepository(repositoryId, newProject); + + return result; + } + + private static async Task CreateColumnHelper(IGitHubClient githubClient, int projectId) + { + var newColumn = new NewProjectColumn(Helper.MakeNameWithTimestamp("new-project-column")); + var result = await githubClient.Repository.Project.Column.Create(projectId, newColumn); + + return result; + } +} + diff --git a/Octokit.Tests.Integration/Clients/ProjectsClientTests.cs b/Octokit.Tests.Integration/Clients/ProjectsClientTests.cs new file mode 100644 index 00000000..64bd5bc2 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/ProjectsClientTests.cs @@ -0,0 +1,402 @@ +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Xunit; + +public class ProjectsClientTests +{ + public class TheGetAllForRepositoryMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheGetAllForRepositoryMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsAllProjectsForRepository() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName); + + Assert.Equal(2, projects.Count); + Assert.True(projects.FirstOrDefault(x => x.Name == project1.Name).Id == project1.Id); + Assert.True(projects.FirstOrDefault(x => x.Name == project2.Name).Id == project2.Id); + } + + [IntegrationTest] + public async Task GetsAllProjectsForRepositoryWithRepositoryId() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId); + + Assert.Equal(2, projects.Count); + Assert.True(projects.FirstOrDefault(x => x.Name == project1.Name).Id == project1.Id); + Assert.True(projects.FirstOrDefault(x => x.Name == project2.Name).Id == project2.Id); + } + + [IntegrationTest] + public async Task GetsAllFilteredProjectsForRepository() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + // Make 2nd project closed + var result = await _github.Repository.Project.Update(project2.Id, new ProjectUpdate { State = ItemState.Closed }); + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, new ProjectRequest(ItemStateFilter.Closed)); + + Assert.Equal(1, projects.Count); + Assert.True(projects.FirstOrDefault(x => x.Name == project2.Name).Id == project2.Id); + } + + [IntegrationTest] + public async Task GetsAllFilteredProjectsForRepositoryWithRepositoryId() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + // Make 2nd project closed + var result = await _github.Repository.Project.Update(project2.Id, new ProjectUpdate { State = ItemState.Closed }); + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId, new ProjectRequest(ItemStateFilter.Closed)); + + Assert.Equal(1, projects.Count); + Assert.True(projects.FirstOrDefault(x => x.Name == project2.Name).Id == project2.Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfProjectsForRepositoryWithoutStart() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, options); + + Assert.Equal(1, projects.Count); + Assert.Equal(project1.Id, projects[0].Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfProjectsForRepositoryWithRepositoryIdWithoutStart() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId, options); + + Assert.Equal(1, projects.Count); + Assert.Equal(project1.Id, projects[0].Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfProjectsForRepositoryWithStart() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, options); + + Assert.Equal(1, projects.Count); + Assert.Equal(project2.Id, projects[0].Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfProjectsForRepositoryWithRepositoryIdWithStart() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId, options); + + Assert.Equal(1, projects.Count); + Assert.Equal(project2.Id, projects[0].Id); + } + + [IntegrationTest] + public async Task ReturnsDistinctProjectsForRepositoryBasedOnStartPage() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, skipStartOptions); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + + [IntegrationTest] + public async Task ReturnsDistinctProjectsForRepositoryBasedOnStartPageWithRepositoryId() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId, skipStartOptions); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheGetAllForOrganizationMethod + { + IGitHubClient _github; + + public TheGetAllForOrganizationMethod() + { + _github = Helper.GetAuthenticatedClient(); + } + + [IntegrationTest] + public async Task GetsAllProjects() + { + var project1 = await CreateOrganizationProjectHelper(_github, Helper.Organization); + var project2 = await CreateOrganizationProjectHelper(_github, Helper.Organization); + + var projects = await _github.Repository.Project.GetAllForOrganization(Helper.Organization); + + Assert.True(projects.FirstOrDefault(x => x.Name == project1.Name).Id == project1.Id); + Assert.True(projects.FirstOrDefault(x => x.Name == project2.Name).Id == project2.Id); + } + + [IntegrationTest] + public async Task GetsAllFilteredProjectsForRepository() + { + var project = await CreateOrganizationProjectHelper(_github, Helper.Organization); + + // Make project closed + var result = await _github.Repository.Project.Update(project.Id, new ProjectUpdate { State = ItemState.Closed }); + + var projects = await _github.Repository.Project.GetAllForOrganization(Helper.Organization, new ProjectRequest(ItemStateFilter.Closed)); + + Assert.True(projects.FirstOrDefault(x => x.Name == project.Name).Id == project.Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfProjectsForOrganization() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 1 + }; + + var projects = await _github.Repository.Project.GetAllForOrganization(Helper.Organization, options); + + Assert.Equal(5, projects.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctProjectsForOrganizationBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _github.Repository.Project.GetAllForOrganization(Helper.Organization, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.Repository.Project.GetAllForOrganization(Helper.Organization, skipStartOptions); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + } + + public class TheGetMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheGetMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsProject() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var result = await _github.Repository.Project.Get(project.Id); + + Assert.NotNull(result); + Assert.Equal(project.Name, result.Name); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheUpdateMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheUpdateMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task UpdatesProject() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var projectUpdate = new ProjectUpdate + { + Name = "newName", + State = ItemState.Closed + }; + + var result = await _github.Repository.Project.Update(project.Id, projectUpdate); + + Assert.Equal("newName", result.Name); + Assert.Equal(ItemState.Closed, result.State); + Assert.Equal(project.Id, result.Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheDeleteMethod : IDisposable + { + IGitHubClient _github; + RepositoryContext _context; + + public TheDeleteMethod() + { + _github = Helper.GetAuthenticatedClient(); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task DeletesProject() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var result = await _github.Repository.Project.Delete(project.Id); + + Assert.True(result); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + private static async Task CreateRepositoryProjectHelper(IGitHubClient githubClient, long repositoryId) + { + var newProject = new NewProject(Helper.MakeNameWithTimestamp("new-project")); + var result = await githubClient.Repository.Project.CreateForRepository(repositoryId, newProject); + + return result; + } + + private static async Task CreateOrganizationProjectHelper(IGitHubClient githubClient, string organization) + { + var newProject = new NewProject(Helper.MakeNameWithTimestamp("new-project")); + var result = await githubClient.Repository.Project.CreateForOrganization(organization, newProject); + + return result; + } +} + diff --git a/Octokit.Tests.Integration/Reactive/ObservableProjectCardsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableProjectCardsClientTests.cs new file mode 100644 index 00000000..d1181610 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableProjectCardsClientTests.cs @@ -0,0 +1,356 @@ +using Octokit; +using Octokit.Reactive; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Xunit; +using System.Reactive.Linq; + +public class ObservableProjectCardsClientTests +{ + public class TheGetAllMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheGetAllMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsAllCards() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column.Id); + var card2 = await CreateCardHelper(_github, column.Id); + + var result = await _github.Repository.Project.Card.GetAll(column.Id).ToList(); + + Assert.Equal(2, result.Count); + Assert.True(result.FirstOrDefault(x => x.Id == card1.Id).Id == card1.Id); + Assert.True(result.FirstOrDefault(x => x.Id == card2.Id).Id == card2.Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfCardWithoutStart() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column.Id); + var card2 = await CreateCardHelper(_github, column.Id); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var cards = await _github.Repository.Project.Card.GetAll(column.Id, options).ToList(); + + // NOTE: cards are returned in reverse order + Assert.Equal(1, cards.Count); + Assert.Equal(card2.Id, cards[0].Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfCardsWithStart() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column.Id); + var card2 = await CreateCardHelper(_github, column.Id); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var cards = await _github.Repository.Project.Card.GetAll(column.Id, options).ToList(); + + // NOTE: cards are returned in reverse order + Assert.Equal(1, cards.Count); + Assert.Equal(card1.Id, cards[0].Id); + } + + [IntegrationTest] + public async Task ReturnsDistinctCardsBasedOnStartPage() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column.Id); + var card2 = await CreateCardHelper(_github, column.Id); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _github.Repository.Project.Card.GetAll(column.Id, startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.Repository.Project.Card.GetAll(column.Id, skipStartOptions).ToList(); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheGetMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheGetMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsCard() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card = await CreateCardHelper(_github, column.Id); + + var result = await _github.Repository.Project.Card.Get(card.Id); + + Assert.Equal(card.Id, result.Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheCreateMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheCreateMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task CreatesCard() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card = await CreateCardHelper(_github, column.Id); + + Assert.NotNull(card); + } + + [IntegrationTest] + public async Task CreatesIssueCard() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var issue = await _github.Issue.Create(_context.RepositoryId, new NewIssue("a test issue")); + var column = await CreateColumnHelper(_github, project.Id); + var card = await CreateIssueCardHelper(_github, issue.Id, column.Id); + + Assert.NotNull(card); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheUpdateMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheUpdateMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task UpdatesCard() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card = await CreateCardHelper(_github, column.Id); + var cardUpdate = new ProjectCardUpdate("newNameOfNote"); + + var result = await _github.Repository.Project.Card.Update(card.Id, cardUpdate); + + Assert.Equal("newNameOfNote", result.Note); + Assert.Equal(card.Id, result.Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheDeleteMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheDeleteMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task DeletesCard() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card = await CreateCardHelper(_github, column.Id); + + var result = await _github.Repository.Project.Card.Delete(card.Id); + + Assert.True(result); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheMoveMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheMoveMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task MovesCardWithinColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column.Id); + var card2 = await CreateCardHelper(_github, column.Id); + var card3 = await CreateCardHelper(_github, column.Id); + + var positionTop = new ProjectCardMove(ProjectCardPosition.Top, column.Id, null); + var positionBottom = new ProjectCardMove(ProjectCardPosition.Top, column.Id, null); + var positionAfter = new ProjectCardMove(ProjectCardPosition.Top, column.Id, card1.Id); + + var resultTop = await _github.Repository.Project.Card.Move(card2.Id, positionTop); + var resultBottom = await _github.Repository.Project.Card.Move(card2.Id, positionBottom); + var resultAfter = await _github.Repository.Project.Card.Move(card2.Id, positionTop); + + Assert.True(resultTop); + Assert.True(resultBottom); + Assert.True(resultAfter); + } + + [IntegrationTest] + public async Task MovesCardBetweenColumns() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + var card1 = await CreateCardHelper(_github, column1.Id); + var card2 = await CreateCardHelper(_github, column1.Id); + var card3 = await CreateCardHelper(_github, column1.Id); + + var positionTop = new ProjectCardMove(ProjectCardPosition.Top, column2.Id, null); + var positionBottom = new ProjectCardMove(ProjectCardPosition.Top, column2.Id, null); + var positionAfter = new ProjectCardMove(ProjectCardPosition.Top, column2.Id, card1.Id); + + var resultTop = await _github.Repository.Project.Card.Move(card1.Id, positionTop); + var resultBottom = await _github.Repository.Project.Card.Move(card2.Id, positionBottom); + var resultAfter = await _github.Repository.Project.Card.Move(card3.Id, positionTop); + + Assert.True(resultTop); + Assert.True(resultBottom); + Assert.True(resultAfter); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + private static async Task CreateRepositoryProjectHelper(IObservableGitHubClient githubClient, long repositoryId) + { + var newProject = new NewProject(Helper.MakeNameWithTimestamp("new-project")); + var result = await githubClient.Repository.Project.CreateForRepository(repositoryId, newProject); + + return result; + } + + private static async Task CreateColumnHelper(IObservableGitHubClient githubClient, int projectId) + { + var newColumn = new NewProjectColumn(Helper.MakeNameWithTimestamp("new-project-column")); + var result = await githubClient.Repository.Project.Column.Create(projectId, newColumn); + + return result; + } + + private static async Task CreateCardHelper(IObservableGitHubClient githubClient, int columnId) + { + var newCard = new NewProjectCard(Helper.MakeNameWithTimestamp("new-card")); + var result = await githubClient.Repository.Project.Card.Create(columnId, newCard); + + return result; + } + + private static async Task CreateIssueCardHelper(IObservableGitHubClient githubClient, int issueId, int columnId) + { + var newCard = new NewProjectCard(issueId, ProjectCardContentType.Issue); + var result = await githubClient.Repository.Project.Card.Create(columnId, newCard); + + return result; + } +} + diff --git a/Octokit.Tests.Integration/Reactive/ObservableProjectColumnsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableProjectColumnsClientTests.cs new file mode 100644 index 00000000..fe9e5e25 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableProjectColumnsClientTests.cs @@ -0,0 +1,295 @@ +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Xunit; +using Octokit.Reactive; +using System.Reactive.Linq; + +public class ObservableProjectColumnsClientTests +{ + public class TheGetAllColumnsMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheGetAllColumnsMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsAllColumns() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + + var result = await _github.Repository.Project.Column.GetAll(project.Id).ToList(); + + Assert.Equal(2, result.Count); + Assert.True(result.FirstOrDefault(x => x.Id == column1.Id).Id == column1.Id); + Assert.True(result.FirstOrDefault(x => x.Id == column2.Id).Id == column2.Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfColumnsWithoutStart() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var columns = await _github.Repository.Project.Column.GetAll(project.Id, options).ToList(); + + Assert.Equal(1, columns.Count); + Assert.Equal(column1.Id, columns[0].Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfColumnsWithStart() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var columns = await _github.Repository.Project.Column.GetAll(project.Id, options).ToList(); + + Assert.Equal(1, columns.Count); + Assert.Equal(column2.Id, columns[0].Id); + } + + [IntegrationTest] + public async Task ReturnsDistinctColumnsBasedOnStartPage() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _github.Repository.Project.Column.GetAll(project.Id, startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.Repository.Project.Column.GetAll(project.Id, skipStartOptions).ToList(); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheGetColumnMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheGetColumnMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + + var result = await _github.Repository.Project.Column.Get(column.Id); + + Assert.Equal(column.Id, result.Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheCreateColumnMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheCreateColumnMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task CreatesColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + + Assert.Equal(project.Url, column.ProjectUrl); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheUpdateColumnMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheUpdateColumnMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task UpdatesColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + + var columnUpdate = new ProjectColumnUpdate("newName"); + + var result = await _github.Repository.Project.Column.Update(column.Id, columnUpdate); + + Assert.Equal("newName", result.Name); + Assert.Equal(column.Id, result.Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheDeleteColumnMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheDeleteColumnMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task DeletesColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column = await CreateColumnHelper(_github, project.Id); + + var result = await _github.Repository.Project.Column.Delete(column.Id); + + Assert.True(result); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheMoveColumnMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheMoveColumnMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task MovesColumn() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var column1 = await CreateColumnHelper(_github, project.Id); + var column2 = await CreateColumnHelper(_github, project.Id); + + var positionFirst = new ProjectColumnMove(ProjectColumnPosition.First, null); + var positionLast = new ProjectColumnMove(ProjectColumnPosition.Last, null); + var positionAfter = new ProjectColumnMove(ProjectColumnPosition.After, column1.Id); + + var resultFirst = await _github.Repository.Project.Column.Move(column2.Id, positionFirst); + var resultLast = await _github.Repository.Project.Column.Move(column2.Id, positionLast); + var resultAfter = await _github.Repository.Project.Column.Move(column2.Id, positionAfter); + + Assert.True(resultFirst); + Assert.True(resultLast); + Assert.True(resultAfter); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + private static async Task CreateRepositoryProjectHelper(IObservableGitHubClient githubClient, long repositoryId) + { + var newProject = new NewProject(Helper.MakeNameWithTimestamp("new-project")); + var result = await githubClient.Repository.Project.CreateForRepository(repositoryId, newProject); + + return result; + } + + private static async Task CreateColumnHelper(IObservableGitHubClient githubClient, int projectId) + { + var newColumn = new NewProjectColumn(Helper.MakeNameWithTimestamp("new-project-column")); + var result = await githubClient.Repository.Project.Column.Create(projectId, newColumn); + + return result; + } +} + diff --git a/Octokit.Tests.Integration/Reactive/ObservableProjectsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableProjectsClientTests.cs new file mode 100644 index 00000000..cdbfa6d4 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableProjectsClientTests.cs @@ -0,0 +1,404 @@ +using Octokit; +using Octokit.Tests.Integration; +using Octokit.Tests.Integration.Helpers; +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Xunit; +using Octokit.Reactive; +using System.Reactive.Linq; + +public class ObservableProjectsClientTests +{ + public class TheGetAllForRepositoryMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheGetAllForRepositoryMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsAllProjectsForRepository() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName).ToList(); + + Assert.Equal(2, projects.Count); + Assert.True(projects.FirstOrDefault(x => x.Name == project1.Name).Id == project1.Id); + Assert.True(projects.FirstOrDefault(x => x.Name == project2.Name).Id == project2.Id); + } + + [IntegrationTest] + public async Task GetsAllProjectsForRepositoryWithRepositoryId() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId).ToList(); + + Assert.Equal(2, projects.Count); + Assert.True(projects.FirstOrDefault(x => x.Name == project1.Name).Id == project1.Id); + Assert.True(projects.FirstOrDefault(x => x.Name == project2.Name).Id == project2.Id); + } + + [IntegrationTest] + public async Task GetsAllFilteredProjectsForRepository() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + // Make 2nd project closed + var result = await _github.Repository.Project.Update(project2.Id, new ProjectUpdate { State = ItemState.Closed }); + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, new ProjectRequest(ItemStateFilter.Closed)).ToList(); + + Assert.Equal(1, projects.Count); + Assert.True(projects.FirstOrDefault(x => x.Name == project2.Name).Id == project2.Id); + } + + [IntegrationTest] + public async Task GetsAllFilteredProjectsForRepositoryWithRepositoryId() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + // Make 2nd project closed + var result = await _github.Repository.Project.Update(project2.Id, new ProjectUpdate { State = ItemState.Closed }); + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId, new ProjectRequest(ItemStateFilter.Closed)).ToList(); + + Assert.Equal(1, projects.Count); + Assert.True(projects.FirstOrDefault(x => x.Name == project2.Name).Id == project2.Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfProjectsForRepositoryWithoutStart() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, options).ToList(); + + Assert.Equal(1, projects.Count); + Assert.Equal(project1.Id, projects[0].Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfProjectsForRepositoryWithRepositoryIdWithoutStart() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId, options).ToList(); + + Assert.Equal(1, projects.Count); + Assert.Equal(project1.Id, projects[0].Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfProjectsForRepositoryWithStart() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, options).ToList(); + + Assert.Equal(1, projects.Count); + Assert.Equal(project2.Id, projects[0].Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfProjectsForRepositoryWithRepositoryIdWithStart() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var projects = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId, options).ToList(); + + Assert.Equal(1, projects.Count); + Assert.Equal(project2.Id, projects[0].Id); + } + + [IntegrationTest] + public async Task ReturnsDistinctProjectsForRepositoryBasedOnStartPage() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.Repository.Project.GetAllForRepository(_context.RepositoryOwner, _context.RepositoryName, skipStartOptions).ToList(); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + + [IntegrationTest] + public async Task ReturnsDistinctProjectsForRepositoryBasedOnStartPageWithRepositoryId() + { + var project1 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + var project2 = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId, startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.Repository.Project.GetAllForRepository(_context.RepositoryId, skipStartOptions).ToList(); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheGetAllForOrganizationMethod + { + IObservableGitHubClient _github; + + public TheGetAllForOrganizationMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + } + + [IntegrationTest] + public async Task GetsAllProjects() + { + var project1 = await CreateOrganizationProjectHelper(_github, Helper.Organization); + var project2 = await CreateOrganizationProjectHelper(_github, Helper.Organization); + + var projects = await _github.Repository.Project.GetAllForOrganization(Helper.Organization).ToList(); + + Assert.True(projects.FirstOrDefault(x => x.Name == project1.Name).Id == project1.Id); + Assert.True(projects.FirstOrDefault(x => x.Name == project2.Name).Id == project2.Id); + } + + [IntegrationTest] + public async Task GetsAllFilteredProjectsForRepository() + { + var project = await CreateOrganizationProjectHelper(_github, Helper.Organization); + + // Make project closed + var result = await _github.Repository.Project.Update(project.Id, new ProjectUpdate { State = ItemState.Closed }); + + var projects = await _github.Repository.Project.GetAllForOrganization(Helper.Organization, new ProjectRequest(ItemStateFilter.Closed)).ToList(); + + Assert.True(projects.FirstOrDefault(x => x.Name == project.Name).Id == project.Id); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfProjectsForOrganization() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 1 + }; + + var projects = await _github.Repository.Project.GetAllForOrganization(Helper.Organization, options).ToList(); + + Assert.Equal(5, projects.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctProjectsForOrganizationBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1 + }; + + var firstPage = await _github.Repository.Project.GetAllForOrganization(Helper.Organization, startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _github.Repository.Project.GetAllForOrganization(Helper.Organization, skipStartOptions).ToList(); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + } + } + + public class TheGetMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheGetMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task GetsProject() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var result = await _github.Repository.Project.Get(project.Id); + + Assert.NotNull(result); + Assert.Equal(project.Name, result.Name); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheUpdateMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheUpdateMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task UpdatesProject() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var projectUpdate = new ProjectUpdate + { + Name = "newName", + State = ItemState.Closed + }; + + var result = await _github.Repository.Project.Update(project.Id, projectUpdate); + + Assert.Equal("newName", result.Name); + Assert.Equal(ItemState.Closed, result.State); + Assert.Equal(project.Id, result.Id); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + public class TheDeleteMethod : IDisposable + { + IObservableGitHubClient _github; + RepositoryContext _context; + + public TheDeleteMethod() + { + _github = new ObservableGitHubClient(Helper.GetAuthenticatedClient()); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = _github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task DeletesProject() + { + var project = await CreateRepositoryProjectHelper(_github, _context.RepositoryId); + + var result = await _github.Repository.Project.Delete(project.Id); + + Assert.True(result); + } + + public void Dispose() + { + if (_context != null) + _context.Dispose(); + } + } + + private static async Task CreateRepositoryProjectHelper(IObservableGitHubClient githubClient, long repositoryId) + { + var newProject = new NewProject(Helper.MakeNameWithTimestamp("new-project")); + var result = await githubClient.Repository.Project.CreateForRepository(repositoryId, newProject); + + return result; + } + + private static async Task CreateOrganizationProjectHelper(IObservableGitHubClient githubClient, string organization) + { + var newProject = new NewProject(Helper.MakeNameWithTimestamp("new-project")); + var result = await githubClient.Repository.Project.CreateForOrganization(organization, newProject); + + return result; + } +} + diff --git a/Octokit.Tests/Clients/DeploymentsClientTests.cs b/Octokit.Tests/Clients/DeploymentsClientTests.cs index 92f37bba..fdd87729 100644 --- a/Octokit.Tests/Clients/DeploymentsClientTests.cs +++ b/Octokit.Tests/Clients/DeploymentsClientTests.cs @@ -102,7 +102,7 @@ public class DeploymentsClientTests } [Fact] - public async Task RequestsCorrectUrlWithRepostoryIdWithApiOptions() + public async Task RequestsCorrectUrlWithRepositoryIdWithApiOptions() { var connection = Substitute.For(); var client = new DeploymentsClient(connection); diff --git a/Octokit.Tests/Clients/ProjectCardsClientTests.cs b/Octokit.Tests/Clients/ProjectCardsClientTests.cs new file mode 100644 index 00000000..06c6a7c1 --- /dev/null +++ b/Octokit.Tests/Clients/ProjectCardsClientTests.cs @@ -0,0 +1,159 @@ +using System; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class ProjectCardsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ProjectCardsClient(null)); + } + } + + public class TheGetAllMethod + { + [Fact] + public async Task RequestCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectCardsClient(connection); + + await client.GetAll(1); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "projects/columns/1/cards"), + Args.EmptyDictionary, + "application/vnd.github.inertia-preview+json", + Args.ApiOptions); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectCardsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAll(1, null)); + } + } + + public class TheGetMethod + { + [Fact] + public async Task RequestCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectCardsClient(connection); + + await client.Get(1); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "projects/columns/cards/1"), + null, + "application/vnd.github.inertia-preview+json"); + } + } + + public class TheCreateMethod + { + [Fact] + public async Task PostsToCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectCardsClient(connection); + var newCard = new NewProjectCard("someNote"); + + await client.Create(1, newCard); + + connection.Received().Post( + Arg.Is(u => u.ToString() == "projects/columns/1/cards"), + newCard, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectCardsClient(Substitute.For()); + var newCard = new NewProjectCard("someNote"); + + await Assert.ThrowsAsync(() => client.Create(1, null)); + } + } + + public class TheUpdateMethod + { + [Fact] + public async Task PostsToCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectCardsClient(connection); + var updateCard = new ProjectCardUpdate("someNewNote"); + + await client.Update(1, updateCard); + + connection.Received().Patch( + Arg.Is(u => u.ToString() == "projects/columns/cards/1"), + updateCard, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectCardsClient(Substitute.For()); + var updateCard = new ProjectCardUpdate("someNewNote"); + + await Assert.ThrowsAsync(() => client.Update(1, null)); + } + } + + public class TheDeleteMethod + { + [Fact] + public async Task DeletesCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectCardsClient(connection); + + await client.Delete(1); + + connection.Connection.Received().Delete( + Arg.Is(u => u.ToString() == "projects/columns/cards/1"), + Arg.Any(), + "application/vnd.github.inertia-preview+json"); + } + } + + public class TheMoveMethod + { + [Fact] + public async Task PostsToCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectCardsClient(connection); + var position = new ProjectCardMove(ProjectCardPosition.Top, 1, null); + + await client.Move(1, position); + + connection.Connection.Received().Post( + Arg.Is(u => u.ToString() == "projects/columns/cards/1/moves"), + position, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectCardsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Move(1, null)); + } + } + } +} diff --git a/Octokit.Tests/Clients/ProjectColumnsClientTests.cs b/Octokit.Tests/Clients/ProjectColumnsClientTests.cs new file mode 100644 index 00000000..7eb3c8fb --- /dev/null +++ b/Octokit.Tests/Clients/ProjectColumnsClientTests.cs @@ -0,0 +1,160 @@ +using System; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class ProjectColumnsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new ProjectColumnsClient(null)); + } + } + + public class TheGetAllMethod + { + [Fact] + public async Task RequestCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectColumnsClient(connection); + + await client.GetAll(1); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "projects/1/columns"), + Args.EmptyDictionary, + "application/vnd.github.inertia-preview+json", + Args.ApiOptions); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectColumnsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAll(1, null)); + } + } + + public class TheGetMethod + { + [Fact] + public async Task RequestCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectColumnsClient(connection); + + await client.Get(1); + + connection.Received().Get( + Arg.Is(u => u.ToString() == "projects/columns/1"), + null, + "application/vnd.github.inertia-preview+json"); + } + } + + public class TheCreateMethod + { + [Fact] + public async Task PostsToCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectColumnsClient(connection); + var newProjectColumn = new NewProjectColumn("someName"); + + await client.Create(1, newProjectColumn); + + connection.Received().Post( + Arg.Is(u => u.ToString() == "projects/1/columns"), + newProjectColumn, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectColumnsClient(Substitute.For()); + var newProjectColumn = new NewProjectColumn("someName"); + + await Assert.ThrowsAsync(() => client.Create(1, null)); + } + } + + public class TheUpdateMethod + { + [Fact] + public async Task PostsToCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectColumnsClient(connection); + var updateProjectColumn = new ProjectColumnUpdate("someNewName"); + + await client.Update(1, updateProjectColumn); + + connection.Received().Patch( + Arg.Is(u => u.ToString() == "projects/columns/1"), + updateProjectColumn, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectColumnsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Update(1, null)); + } + } + + public class TheDeleteMethod + { + [Fact] + public async Task DeletesCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectColumnsClient(connection); + + await client.Delete(1); + + connection.Connection.Received().Delete( + Arg.Is(u => u.ToString() == "projects/columns/1"), + Arg.Any(), + "application/vnd.github.inertia-preview+json"); + } + } + + public class TheMoveMethod + { + [Fact] + public async Task PostsToCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectColumnsClient(connection); + var position = new ProjectColumnMove(ProjectColumnPosition.First, null); + + await client.Move(1, position); + + connection.Connection.Received().Post( + Arg.Is(u => u.ToString() == "projects/columns/1/moves"), + position, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectColumnsClient(Substitute.For()); + var position = new ProjectColumnMove(ProjectColumnPosition.First, null); + + await Assert.ThrowsAsync(() => client.Move(1, null)); + } + } + } +} diff --git a/Octokit.Tests/Clients/ProjectsClientTests.cs b/Octokit.Tests/Clients/ProjectsClientTests.cs new file mode 100644 index 00000000..193b6b5e --- /dev/null +++ b/Octokit.Tests/Clients/ProjectsClientTests.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class ProjectsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new ProjectsClient(null)); + } + } + + public class TheGetAllForRepositoryMethod + { + [Fact] + public async Task RequestCorrectUrl() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + + await client.GetAllForRepository("owner", "repo"); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repos/owner/repo/projects"), + Args.EmptyDictionary, + "application/vnd.github.inertia-preview+json", + Args.ApiOptions); + } + + [Fact] + public async Task RequestCorrectUrlWithRequestParameter() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + + await client.GetAllForRepository("owner", "repo", new ProjectRequest(ItemStateFilter.All)); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repos/owner/repo/projects"), + Arg.Is>(d => d.ContainsKey("state")), + "application/vnd.github.inertia-preview+json", + Args.ApiOptions); + } + + [Fact] + public async Task RequestCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + + await client.GetAllForRepository(1); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repositories/1/projects"), + Args.EmptyDictionary, + "application/vnd.github.inertia-preview+json", + Args.ApiOptions); + } + + [Fact] + public async Task RequestCorrectUrlWithRequestParameterWithRepositoryId() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + + await client.GetAllForRepository(1, new ProjectRequest(ItemStateFilter.All)); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repositories/1/projects"), + Arg.Is>(d => d.ContainsKey("state")), + "application/vnd.github.inertia-preview+json", + Args.ApiOptions); + } + + [Fact] + public async Task EnsureNonNullOrEmptyArguments() + { + var client = new ProjectsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "repo")); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "repo", (ProjectRequest)null)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "repo", (ApiOptions)null)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "repo", new ProjectRequest(ItemStateFilter.All), null)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "repo", null, ApiOptions.None)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "repo")); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "")); + } + + [Fact] + public async Task EnsureNonNullArgumentsWithRepositoryId() + { + var client = new ProjectsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(1, (ApiOptions)null)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(1, (ProjectRequest)null)); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(1, new ProjectRequest(ItemStateFilter.All), null)); + await Assert.ThrowsAsync(() => client.GetAllForRepository(1, null, ApiOptions.None)); + } + } + + public class TheGetAllForOrganizationMethod + { + [Fact] + public async Task RequestCorrectUrl() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + + await client.GetAllForOrganization("org"); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "orgs/org/projects"), + Args.EmptyDictionary, + "application/vnd.github.inertia-preview+json", + Args.ApiOptions); + } + + [Fact] + public async Task RequestCorrectUrlWithRequestParameter() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + + await client.GetAllForOrganization("org", new ProjectRequest(ItemStateFilter.Closed)); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "orgs/org/projects"), + Arg.Is>(d => d.ContainsKey("state")), + "application/vnd.github.inertia-preview+json", + Args.ApiOptions); + } + + [Fact] + public async Task EnsureNonNullOrEmptyArguments() + { + var client = new ProjectsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("")); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", (ApiOptions)null)); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, new ProjectRequest(ItemStateFilter.All))); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", new ProjectRequest(ItemStateFilter.All))); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", (ProjectRequest)null)); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, new ProjectRequest(ItemStateFilter.All), ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", new ProjectRequest(ItemStateFilter.All), ApiOptions.None)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", new ProjectRequest(ItemStateFilter.All), null)); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", null, ApiOptions.None)); + } + } + + public class TheGetMethod + { + [Fact] + public async Task RequestCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + + await client.Get(1); + + connection.Received().Get(Arg.Is(u => u.ToString() == "projects/1"), null, "application/vnd.github.inertia-preview+json"); + } + } + + public class TheCreateForRepositoryMethod + { + [Fact] + public async Task PostsToCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + var newProject = new NewProject("someName"); + + await client.CreateForRepository(1, newProject); + + connection.Received().Post(Arg.Is(u => u.ToString() == "repositories/1/projects"), newProject, "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.CreateForRepository(1, null)); + } + } + + public class TheCreateForOrganizationMethod + { + [Fact] + public async Task PostsToCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + var newProject = new NewProject("someName"); + + await client.CreateForOrganization("org", newProject); + + connection.Received().Post(Arg.Is(u => u.ToString() == "orgs/org/projects"), newProject, "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectsClient(Substitute.For()); + var newProject = new NewProject("someName"); + + await Assert.ThrowsAsync(() => client.CreateForOrganization("", newProject)); + await Assert.ThrowsAsync(() => client.CreateForOrganization("org", null)); + await Assert.ThrowsAsync(() => client.CreateForOrganization(null, newProject)); + } + } + + public class TheUpdateMethod + { + [Fact] + public async Task PostsToCorrectURL() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + var updateProject = new ProjectUpdate(); + + await client.Update(1, updateProject); + + connection.Received().Patch(Arg.Is(u => u.ToString() == "projects/1"), updateProject, "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ProjectsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.Update(1, null)); + } + } + + public class TheDeleteMethod + { + [Fact] + public async Task DeletesCorrectUrl() + { + var connection = Substitute.For(); + var client = new ProjectsClient(connection); + + await client.Delete(1); + + connection.Connection.Received().Delete(Arg.Is(u => u.ToString() == "projects/1"), Arg.Any(), "application/vnd.github.inertia-preview+json"); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableProjectCardsClientTests.cs b/Octokit.Tests/Reactive/ObservableProjectCardsClientTests.cs new file mode 100644 index 00000000..8be07b0e --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableProjectCardsClientTests.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableProjectCardsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new ObservableProjectCardsClient(null)); + } + } + + public class TheGetAllMethod + { + [Fact] + public void RequestCorrectURL() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableProjectCardsClient(gitHubClient); + + client.GetAll(1); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "projects/columns/1/cards"), + Args.EmptyDictionary, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectCardsClient(gitHubClient); + + await Assert.ThrowsAsync(() => client.GetAll(1, null).ToTask()); + } + } + + public class TheGetMethod + { + [Fact] + public void RequestCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectCardsClient(gitHubClient); + + client.Get(1); + + gitHubClient.Repository.Project.Card.Received().Get(1); + } + } + + public class TheCreateMethod + { + [Fact] + public void PostsToCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectCardsClient(gitHubClient); + var newCard = new NewProjectCard("someNote"); + + client.Create(1, newCard); + + gitHubClient.Repository.Project.Card.Received().Create(1, newCard); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableProjectCardsClient(Substitute.For()); + var newCard = new NewProjectCard("someNote"); + + await Assert.ThrowsAsync(() => client.Create(1, null).ToTask()); + } + } + + public class TheUpdateMethod + { + [Fact] + public void PostsToCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectCardsClient(gitHubClient); + var updateCard = new ProjectCardUpdate("someNewNote"); + + client.Update(1, updateCard); + + gitHubClient.Repository.Project.Card.Received().Update(1, updateCard); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableProjectCardsClient(Substitute.For()); + var updateCard = new ProjectCardUpdate("someNewNote"); + + await Assert.ThrowsAsync(() => client.Update(1, null).ToTask()); + } + } + + public class TheDeleteMethod + { + [Fact] + public void DeletesCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectCardsClient(gitHubClient); + + client.Delete(1); + + gitHubClient.Repository.Project.Card.Received().Delete(1); + } + } + + public class TheMoveMethod + { + [Fact] + public void PostsToCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectCardsClient(gitHubClient); + var position = new ProjectCardMove(ProjectCardPosition.Top, 1, null); + + client.Move(1, position); + + gitHubClient.Repository.Project.Card.Received().Move(1, position); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableProjectCardsClient(Substitute.For()); + var position = new ProjectCardMove(ProjectCardPosition.Top, 1, null); + + await Assert.ThrowsAsync(() => client.Move(1, null).ToTask()); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableProjectColumnsClientTests.cs b/Octokit.Tests/Reactive/ObservableProjectColumnsClientTests.cs new file mode 100644 index 00000000..39f24b82 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableProjectColumnsClientTests.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableProjectColumnsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new ObservableProjectColumnsClient(null)); + } + } + + public class TheGetAllMethod + { + [Fact] + public void RequestCorrectURL() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableProjectColumnsClient(gitHubClient); + + client.GetAll(1); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "projects/1/columns"), + Args.EmptyDictionary, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectColumnsClient(gitHubClient); + + await Assert.ThrowsAsync(() => client.GetAll(1, null).ToTask()); + } + } + + public class TheGetMethod + { + [Fact] + public void RequestCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectColumnsClient(gitHubClient); + + client.Get(1); + + gitHubClient.Repository.Project.Column.Received().Get(1); + } + } + + public class TheCreateMethod + { + [Fact] + public void PostsToCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectColumnsClient(gitHubClient); + var newProjectColumn = new NewProjectColumn("someName"); + + client.Create(1, newProjectColumn); + + gitHubClient.Repository.Project.Column.Received().Create(1, newProjectColumn); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableProjectColumnsClient(Substitute.For()); + var newProjectColumn = new NewProjectColumn("someName"); + + Assert.Throws(() => client.Create(1, null)); + } + } + + public class TheUpdateMethod + { + [Fact] + public void PostsToCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectColumnsClient(gitHubClient); + var updatePorjectColumn = new ProjectColumnUpdate("someNewName"); + + client.Update(1, updatePorjectColumn); + + gitHubClient.Repository.Project.Column.Received().Update(1, updatePorjectColumn); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableProjectColumnsClient(Substitute.For()); + var updateProjectColumn = new ProjectColumnUpdate("someNewName"); + + Assert.Throws(() => client.Update(1, null)); + } + } + + public class TheDeleteMethod + { + [Fact] + public void DeletesCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectColumnsClient(gitHubClient); + + client.Delete(1); + + gitHubClient.Repository.Project.Column.Received().Delete(1); + } + } + + public class TheMoveMethod + { + [Fact] + public void PostsToCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectColumnsClient(gitHubClient); + var position = new ProjectColumnMove(ProjectColumnPosition.First, null); + + client.Move(1, position); + + gitHubClient.Repository.Project.Column.Received().Move(1, position); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableProjectColumnsClient(Substitute.For()); + var position = new ProjectColumnMove(ProjectColumnPosition.First, null); + + Assert.Throws(() => client.Move(1, null)); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableProjectsClientTests.cs b/Octokit.Tests/Reactive/ObservableProjectsClientTests.cs new file mode 100644 index 00000000..cf7fdf69 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableProjectsClientTests.cs @@ -0,0 +1,279 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableProjectsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new ObservableProjectsClient(null)); + } + } + + public class TheGetAllForRepositoryMethod + { + [Fact] + public void RequestCorrectUrl() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableProjectsClient(gitHubClient); + + client.GetAllForRepository("owner", "repo"); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "repos/owner/repo/projects"), + Args.EmptyDictionary, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public void RequestCorrectUrlWithRequestParameter() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableProjectsClient(gitHubClient); + + client.GetAllForRepository("owner", "repo", new ProjectRequest(ItemStateFilter.All)); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "repos/owner/repo/projects"), + Arg.Is>(d => d.ContainsKey("state")), + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public void RequestCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableProjectsClient(gitHubClient); + + client.GetAllForRepository(1); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "repositories/1/projects"), + Args.EmptyDictionary, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public void RequestCorrectUrlWithRequestParameterWithRepositoryId() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableProjectsClient(gitHubClient); + + client.GetAllForRepository(1, new ProjectRequest(ItemStateFilter.All)); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "repositories/1/projects"), + Arg.Is>(d => d.ContainsKey("state")), + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsureNonNullOrEmptyArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectsClient(gitHubClient); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "repo").ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "repo", (ProjectRequest)null).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "repo", (ApiOptions)null).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "repo", new ProjectRequest(ItemStateFilter.All), null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "repo", null, ApiOptions.None).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository("", "repo").ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "").ToTask()); + } + + [Fact] + public async Task EnsureNonNullArgumentsWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectsClient(gitHubClient); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(1, (ApiOptions)null).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(1, (ProjectRequest)null).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForRepository(1, new ProjectRequest(ItemStateFilter.All), null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForRepository(1, null, ApiOptions.None).ToTask()); + } + } + + public class TheGetAllForOrganizationMethod + { + [Fact] + public void RequestCorrectUrl() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableProjectsClient(gitHubClient); + + client.GetAllForOrganization("org"); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "orgs/org/projects"), + Args.EmptyDictionary, + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public void RequestCorrectUrlWithRequestParameter() + { + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableProjectsClient(gitHubClient); + + client.GetAllForOrganization("org", new ProjectRequest(ItemStateFilter.Closed)); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "orgs/org/projects"), + Arg.Is>(d => d.ContainsKey("state")), + "application/vnd.github.inertia-preview+json"); + } + + [Fact] + public async Task EnsureNonNullOrEmptyArguments() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectsClient(gitHubClient); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("").ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, ApiOptions.None).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", ApiOptions.None).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", (ApiOptions)null).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, new ProjectRequest(ItemStateFilter.All)).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", new ProjectRequest(ItemStateFilter.All)).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", (ProjectRequest)null).ToTask()); + + await Assert.ThrowsAsync(() => client.GetAllForOrganization(null, new ProjectRequest(ItemStateFilter.All), ApiOptions.None).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("", new ProjectRequest(ItemStateFilter.All), ApiOptions.None).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", new ProjectRequest(ItemStateFilter.All), null).ToTask()); + await Assert.ThrowsAsync(() => client.GetAllForOrganization("org", null, ApiOptions.None).ToTask()); + } + } + + public class TheGetMethod + { + [Fact] + public void RequestCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectsClient(gitHubClient); + + client.Get(1); + + gitHubClient.Repository.Project.Received().Get(1); + } + } + + public class TheCreateForRepositoryMethod + { + [Fact] + public void PostsToCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectsClient(gitHubClient); + var newProject = new NewProject("someName"); + + client.CreateForRepository(1, newProject); + + gitHubClient.Repository.Project.Received().CreateForRepository(1, newProject); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableProjectsClient(Substitute.For()); + var newProject = new NewProject("someName"); + + await Assert.ThrowsAsync(() => client.CreateForRepository(1, null).ToTask()); + } + } + + public class TheCreateForOrganizationMethod + { + [Fact] + public void PostsToCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectsClient(gitHubClient); + var newProject = new NewProject("someName"); + + client.CreateForOrganization("org", newProject); + + gitHubClient.Repository.Project.Received().CreateForOrganization("org", newProject); + } + + [Fact] + public async Task EnsureNonNullOrEmptyArguments() + { + var client = new ObservableProjectsClient(Substitute.For()); + var newProject = new NewProject("someName"); + + await Assert.ThrowsAsync(() => client.CreateForOrganization(null, newProject).ToTask()); + await Assert.ThrowsAsync(() => client.CreateForOrganization("", newProject).ToTask()); + await Assert.ThrowsAsync(() => client.CreateForOrganization("org", null).ToTask()); + } + } + + public class TheUpdateMethod + { + [Fact] + public void PostsToCorrectURL() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectsClient(gitHubClient); + var updateProject = new ProjectUpdate { Name = "someNewName" }; + + client.Update(1, updateProject); + + gitHubClient.Repository.Project.Received().Update(1, updateProject); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableProjectsClient(Substitute.For()); + var updateProject = new ProjectUpdate { Name = "someNewName" }; + + await Assert.ThrowsAsync(() => client.Update(1, null).ToTask()); + } + } + + public class TheDeleteMethod + { + [Fact] + public void DeletesCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableProjectsClient(gitHubClient); + + client.Delete(1); + + gitHubClient.Repository.Project.Received().Delete(1); + } + } + } +} diff --git a/Octokit/Clients/IProjectCardsClient.cs b/Octokit/Clients/IProjectCardsClient.cs new file mode 100644 index 00000000..a54e5a74 --- /dev/null +++ b/Octokit/Clients/IProjectCardsClient.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Project Cards API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public interface IProjectCardsClient + { + + /// + /// Gets all cards. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + Task> GetAll(int columnId); + + /// + /// Gets all cards. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// Options for changing the API response + Task> GetAll(int columnId, ApiOptions options); + + /// + /// Gets a single card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] + Task Get(int id); + + /// + /// Creates a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// The card to create + Task Create(int columnId, NewProjectCard newProjectCard); + + /// + /// Updates a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + /// New values to update the card with + Task Update(int id, ProjectCardUpdate projectCardUpdate); + + /// + /// Deletes a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + Task Delete(int id); + + /// + /// Moves a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + /// The position to move the card + Task Move(int id, ProjectCardMove position); + } +} diff --git a/Octokit/Clients/IProjectColumnsClient.cs b/Octokit/Clients/IProjectColumnsClient.cs new file mode 100644 index 00000000..bd9ca8bc --- /dev/null +++ b/Octokit/Clients/IProjectColumnsClient.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Project Columns API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public interface IProjectColumnsClient + { + /// + /// Gets all columns. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + Task> GetAll(int projectId); + + /// + /// Gets all columns. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// Options for changing the API response + Task> GetAll(int projectId, ApiOptions options); + + /// + /// Gets a single column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] + Task Get(int id); + + /// + /// Creates a column. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// The column to create + Task Create(int projectId, NewProjectColumn newProjectColumn); + + /// + /// Updates a column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// New values to update the column with + Task Update(int id, ProjectColumnUpdate projectColumnUpdate); + + /// + /// Deletes a column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + Task Delete(int id); + + /// + /// Moves a column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// The position to move the column + Task Move(int id, ProjectColumnMove position); + } +} diff --git a/Octokit/Clients/IProjectsClient.cs b/Octokit/Clients/IProjectsClient.cs new file mode 100644 index 00000000..c3edd5ae --- /dev/null +++ b/Octokit/Clients/IProjectsClient.cs @@ -0,0 +1,204 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Repository Projects API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public interface IProjectsClient + { + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + Task> GetAllForRepository(string owner, string name); + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter the list of projects returned + Task> GetAllForRepository(string owner, string name, ProjectRequest request); + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + Task> GetAllForRepository(long repositoryId); + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Used to filter the list of projects returned + Task> GetAllForRepository(long repositoryId, ProjectRequest request); + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + Task> GetAllForRepository(string owner, string name, ApiOptions options); + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter the list of projects returned + /// Options for changing the API response + Task> GetAllForRepository(string owner, string name, ProjectRequest request, ApiOptions options); + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Options for changing the API response + Task> GetAllForRepository(long repositoryId, ApiOptions options); + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Used to filter the list of projects returned + /// Options for changing the API response + Task> GetAllForRepository(long repositoryId, ProjectRequest request, ApiOptions options); + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + Task> GetAllForOrganization(string organization); + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Used to filter the list of projects returned + Task> GetAllForOrganization(string organization, ProjectRequest request); + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Options for changing the API response + Task> GetAllForOrganization(string organization, ApiOptions options); + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Used to filter the list of projects returned + /// Options for changing the API response + Task> GetAllForOrganization(string organization, ProjectRequest request, ApiOptions options); + + /// + /// Gets a single project for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] + Task Get(int id); + + /// + /// Creates a project for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the repository + /// The new project to create for this repository + Task CreateForRepository(long repositoryId, NewProject newProject); + + /// + /// Creates a project for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The new project to create for this repository + Task CreateForOrganization(string organization, NewProject newProject); + + /// + /// Updates a project for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// The modified project + Task Update(int id, ProjectUpdate projectUpdate); + + /// + /// Deletes a project. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + Task Delete(int id); + + /// + /// A client for GitHub's Project Cards API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + IProjectCardsClient Card { get; } + + /// + /// A client for GitHub's Project Columns API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + IProjectColumnsClient Column { get; } + } +} diff --git a/Octokit/Clients/IRepositoriesClient.cs b/Octokit/Clients/IRepositoriesClient.cs index a27e31a6..e1abd963 100644 --- a/Octokit/Clients/IRepositoriesClient.cs +++ b/Octokit/Clients/IRepositoriesClient.cs @@ -563,5 +563,13 @@ namespace Octokit /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/traffic/ /// IRepositoryTrafficClient Traffic { get; } + + /// + /// Access GitHub's Repository Projects API + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/projects/ + /// + IProjectsClient Project { get; } } } diff --git a/Octokit/Clients/ProjectCardsClient.cs b/Octokit/Clients/ProjectCardsClient.cs new file mode 100644 index 00000000..2bae4217 --- /dev/null +++ b/Octokit/Clients/ProjectCardsClient.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Project Cards API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public class ProjectCardsClient : ApiClient, IProjectCardsClient + { + public ProjectCardsClient(IApiConnection apiConnection) : + base(apiConnection) + { + } + + /// + /// Gets all cards. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + public Task> GetAll(int columnId) + { + return GetAll(columnId, ApiOptions.None); + } + + /// + /// Gets all cards. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// Options for changing the API response + public Task> GetAll(int columnId, ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.ProjectCards(columnId), new Dictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Gets a single card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + public Task Get(int id) + { + return ApiConnection.Get(ApiUrls.ProjectCard(id), null, AcceptHeaders.ProjectsApiPreview); + } + + /// + /// Creates a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// The card to create + public Task Create(int columnId, NewProjectCard newProjectCard) + { + Ensure.ArgumentNotNull(newProjectCard, "newProjectCard"); + + return ApiConnection.Post(ApiUrls.ProjectCards(columnId), newProjectCard, AcceptHeaders.ProjectsApiPreview); + } + + /// + /// Updates a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + /// New values to update the card with + public Task Update(int id, ProjectCardUpdate projectCardUpdate) + { + Ensure.ArgumentNotNull(projectCardUpdate, "projectCardUpdate"); + + return ApiConnection.Patch(ApiUrls.ProjectCard(id), projectCardUpdate, AcceptHeaders.ProjectsApiPreview); + } + + /// + /// Deletes a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + public async Task Delete(int id) + { + var endpoint = ApiUrls.ProjectCard(id); + + try + { + var httpStatusCode = await Connection.Delete(endpoint, new object(), AcceptHeaders.ProjectsApiPreview).ConfigureAwait(false); + return httpStatusCode == HttpStatusCode.NoContent; + } + catch (NotFoundException) + { + return false; + } + } + + /// + /// Moves a card. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the card + /// The position to move the card + public async Task Move(int id, ProjectCardMove position) + { + Ensure.ArgumentNotNull(position, "position"); + + var endpoint = ApiUrls.ProjectCardMove(id); + try + { + var httpStatusCode = await Connection.Post(endpoint, position, AcceptHeaders.ProjectsApiPreview).ConfigureAwait(false); + return httpStatusCode == HttpStatusCode.Created; + } + catch (NotFoundException) + { + return false; + } + } + } +} diff --git a/Octokit/Clients/ProjectColumnsClient.cs b/Octokit/Clients/ProjectColumnsClient.cs new file mode 100644 index 00000000..66453492 --- /dev/null +++ b/Octokit/Clients/ProjectColumnsClient.cs @@ -0,0 +1,134 @@ +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Project Columns API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public class ProjectColumnsClient : ApiClient, IProjectColumnsClient + { + public ProjectColumnsClient(IApiConnection apiConnection) : + base(apiConnection) + { + } + + /// + /// Gets all columns for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + public Task> GetAll(int projectId) + { + return GetAll(projectId, ApiOptions.None); + } + + /// + /// Gets all columns for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// Options for changing the API response + public Task> GetAll(int projectId, ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.ProjectColumns(projectId), new Dictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Gets a single column for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + public Task Get(int id) + { + return ApiConnection.Get(ApiUrls.ProjectColumn(id), null, AcceptHeaders.ProjectsApiPreview); + } + + /// + /// Creates a column for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// The column to create + public Task Create(int projectId, NewProjectColumn newProjectColumn) + { + Ensure.ArgumentNotNull(newProjectColumn, "newProjectColumn"); + + return ApiConnection.Post(ApiUrls.ProjectColumns(projectId), newProjectColumn, AcceptHeaders.ProjectsApiPreview); + } + + /// + /// Updates a column for this project. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// New values to update the column with + public Task Update(int id, ProjectColumnUpdate projectColumnUpdate) + { + Ensure.ArgumentNotNull(projectColumnUpdate, "projectColumnUpdate"); + + return ApiConnection.Patch(ApiUrls.ProjectColumn(id), projectColumnUpdate, AcceptHeaders.ProjectsApiPreview); + } + + /// + /// Deletes a column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + public async Task Delete(int id) + { + var endpoint = ApiUrls.ProjectColumn(id); + try + { + var httpStatusCode = await Connection.Delete(endpoint, new object(), AcceptHeaders.ProjectsApiPreview).ConfigureAwait(false); + return httpStatusCode == HttpStatusCode.NoContent; + } + catch (NotFoundException) + { + return false; + } + } + + /// + /// Moves a column. + /// + /// + /// See the API documentation for more information. + /// + /// The id of the column + /// The position to move the column + public async Task Move(int id, ProjectColumnMove position) + { + Ensure.ArgumentNotNull(position, "position"); + + var endpoint = ApiUrls.ProjectColumnMove(id); + try + { + var httpStatusCode = await Connection.Post(endpoint, position, AcceptHeaders.ProjectsApiPreview).ConfigureAwait(false); + return httpStatusCode == HttpStatusCode.Created; + } + catch (NotFoundException) + { + return false; + } + } + } +} diff --git a/Octokit/Clients/ProjectsClient.cs b/Octokit/Clients/ProjectsClient.cs new file mode 100644 index 00000000..4bd50ec6 --- /dev/null +++ b/Octokit/Clients/ProjectsClient.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Repository Projects API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public class ProjectsClient : ApiClient, IProjectsClient + { + public ProjectsClient(IApiConnection apiConnection) : + base(apiConnection) + { + Card = new ProjectCardsClient(apiConnection); + Column = new ProjectColumnsClient(apiConnection); + } + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + public Task> GetAllForRepository(string owner, string name) + { + return GetAllForRepository(owner, name, ApiOptions.None); + } + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Options for changing the API response + public Task> GetAllForRepository(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.RepositoryProjects(owner, name), new Dictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter the list of projects returned + public Task> GetAllForRepository(string owner, string name, ProjectRequest request) + { + return GetAllForRepository(owner, name, request, ApiOptions.None); + } + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The owner of the repository + /// The name of the repository + /// Used to filter the list of projects returned + /// Options for changing the API response + public Task> GetAllForRepository(string owner, string name, ProjectRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.RepositoryProjects(owner, name), request.ToParametersDictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + public Task> GetAllForRepository(long repositoryId) + { + return GetAllForRepository(repositoryId, ApiOptions.None); + } + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Options for changing the API response + public Task> GetAllForRepository(long repositoryId, ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.RepositoryProjects(repositoryId), new Dictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Used to filter the list of projects returned + public Task> GetAllForRepository(long repositoryId, ProjectRequest request) + { + return GetAllForRepository(repositoryId, request, ApiOptions.None); + } + + /// + /// Get all projects for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// Used to filter the list of projects returned + /// Options for changing the API response + public Task> GetAllForRepository(long repositoryId, ProjectRequest request, ApiOptions options) + { + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.RepositoryProjects(repositoryId), request.ToParametersDictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + public Task> GetAllForOrganization(string organization) + { + return GetAllForOrganization(organization, ApiOptions.None); + } + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Options for changing the API response + public Task> GetAllForOrganization(string organization, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.OrganizationProjects(organization), new Dictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Used to filter the list of projects returned + public Task> GetAllForOrganization(string organization, ProjectRequest request) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(request, "request"); + + return GetAllForOrganization(organization, request, ApiOptions.None); + } + + /// + /// Get all projects for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organziation + /// Used to filter the list of projects returned + /// Options for changing the API response + public Task> GetAllForOrganization(string organization, ProjectRequest request, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(request, "request"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.OrganizationProjects(organization), request.ToParametersDictionary(), AcceptHeaders.ProjectsApiPreview, options); + } + + /// + /// Gets a single project for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + public Task Get(int id) + { + return ApiConnection.Get(ApiUrls.Project(id), null, AcceptHeaders.ProjectsApiPreview); + } + + /// + /// Creates a project for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the repository + /// The new project to create for this repository + public Task CreateForRepository(long repositoryId, NewProject newProject) + { + Ensure.ArgumentNotNull(newProject, "newProject"); + + return ApiConnection.Post(ApiUrls.RepositoryProjects(repositoryId), newProject, AcceptHeaders.ProjectsApiPreview); + } + + /// + /// Creates a project for the specified organization. + /// + /// + /// See the API documentation for more information. + /// + /// The name of the organization + /// The new project to create for this repository + public Task CreateForOrganization(string organization, NewProject newProject) + { + Ensure.ArgumentNotNullOrEmptyString(organization, "organization"); + Ensure.ArgumentNotNull(newProject, "newProject"); + + return ApiConnection.Post(ApiUrls.OrganizationProjects(organization), newProject, AcceptHeaders.ProjectsApiPreview); + } + + /// + /// Updates a project for this repository. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + /// The modified project + public Task Update(int id, ProjectUpdate projectUpdate) + { + Ensure.ArgumentNotNull(projectUpdate, "projectUpdate"); + + return ApiConnection.Patch(ApiUrls.Project(id), projectUpdate, AcceptHeaders.ProjectsApiPreview); + } + + /// + /// Deletes a project. + /// + /// + /// See the API documentation for more information. + /// + /// The Id of the project + public async Task Delete(int id) + { + var endpoint = ApiUrls.Project(id); + + try + { + var httpStatusCode = await Connection.Delete(endpoint, new object(), AcceptHeaders.ProjectsApiPreview).ConfigureAwait(false); + return httpStatusCode == HttpStatusCode.NoContent; + } + catch (NotFoundException) + { + return false; + } + } + + /// + /// A client for GitHub's Project Cards API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public IProjectCardsClient Card { get; private set; } + + /// + /// A client for GitHub's Project Columns API. + /// + /// + /// See the Repository Projects API documentation for more information. + /// + public IProjectColumnsClient Column { get; private set; } + } +} diff --git a/Octokit/Clients/RepositoriesClient.cs b/Octokit/Clients/RepositoriesClient.cs index 0b8fd00a..e7a9b044 100644 --- a/Octokit/Clients/RepositoriesClient.cs +++ b/Octokit/Clients/RepositoriesClient.cs @@ -38,6 +38,7 @@ namespace Octokit Invitation = new RepositoryInvitationsClient(apiConnection); Branch = new RepositoryBranchesClient(apiConnection); Traffic = new RepositoryTrafficClient(apiConnection); + Project = new ProjectsClient(apiConnection); } /// @@ -830,5 +831,13 @@ namespace Octokit /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/traffic/ /// public IRepositoryTrafficClient Traffic { get; private set; } + + /// + /// Access GitHub's Repository Projects API + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/projects/ + /// + public IProjectsClient Project { get; private set; } } } diff --git a/Octokit/Helpers/AcceptHeaders.cs b/Octokit/Helpers/AcceptHeaders.cs index 1e53eb0e..cb78783d 100644 --- a/Octokit/Helpers/AcceptHeaders.cs +++ b/Octokit/Helpers/AcceptHeaders.cs @@ -44,5 +44,7 @@ namespace Octokit public const string RepositoryTrafficApiPreview = "application/vnd.github.spiderman-preview"; public const string PullRequestReviewsApiPreview = "application/vnd.github.black-cat-preview+json"; + + public const string ProjectsApiPreview = "application/vnd.github.inertia-preview+json"; } } diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 00a430bb..a5c45318 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -3381,5 +3381,106 @@ namespace Octokit { return "repositories/{0}/pulls/{1}/requested_reviewers".FormatUri(repositoryId, number); } + + /// + /// Returns the for the specified project projects. + /// + /// The owner of the repository + /// The name of the repository + /// The for projects. + public static Uri RepositoryProjects(string owner, string repo) + { + return "repos/{0}/{1}/projects".FormatUri(owner, repo); + } + + /// + /// Returns the for the specified project projects. + /// + /// The id of the repository + /// The for projects. + public static Uri RepositoryProjects(long repositoryId) + { + return "repositories/{0}/projects".FormatUri(repositoryId); + } + + /// + /// Returns the for the specified organization projects. + /// + /// The name of the organization + /// The for projects. + public static Uri OrganizationProjects(string organization) + { + return "orgs/{0}/projects".FormatUri(organization); + } + + /// + /// Returns the for a project. + /// + /// The id of the project + /// The for repository projects. + public static Uri Project(int id) + { + return "projects/{0}".FormatUri(id); + } + + /// + /// Returns the for project columns. + /// + /// The id of the columns + /// The for project columns. + public static Uri ProjectColumn(int id) + { + return "projects/columns/{0}".FormatUri(id); + } + + /// + /// Returns the for a specific project column. + /// + /// The id of the project + /// The for a specific project column. + public static Uri ProjectColumns(int projectId) + { + return "projects/{0}/columns".FormatUri(projectId); + } + + /// + /// Returns the to move a project column. + /// + /// The id of the column to move + /// The to move a project column. + public static Uri ProjectColumnMove(int id) + { + return "projects/columns/{0}/moves".FormatUri(id); + } + + /// + /// Returns the for project cards. + /// + /// The id of the card + /// The for project cards. + public static Uri ProjectCard(int id) + { + return "projects/columns/cards/{0}".FormatUri(id); + } + + /// + /// Returns the for project cards. + /// + /// The id of the column + /// The for project cards. + public static Uri ProjectCards(int columnId) + { + return "projects/columns/{0}/cards".FormatUri(columnId); + } + + /// + /// Returns the to move a project card. + /// + /// The id of the card to move + /// The to move a project card. + public static Uri ProjectCardMove(int id) + { + return "projects/columns/cards/{0}/moves".FormatUri(id); + } } } diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index 6a1bdaca..7a5862ea 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -222,6 +222,14 @@ namespace Octokit return response.HttpResponse.StatusCode; } + public async Task Post(Uri uri, object body, string accepts) + { + Ensure.ArgumentNotNull(uri, "uri"); + + var response = await SendData(uri, HttpMethod.Post, body, accepts, null, CancellationToken.None).ConfigureAwait(false); + return response.HttpResponse.StatusCode; + } + public Task> Post(Uri uri) { Ensure.ArgumentNotNull(uri, "uri"); diff --git a/Octokit/Http/IConnection.cs b/Octokit/Http/IConnection.cs index 5ae1c1e1..96bccd8b 100644 --- a/Octokit/Http/IConnection.cs +++ b/Octokit/Http/IConnection.cs @@ -96,9 +96,18 @@ namespace Octokit /// Performs an asynchronous HTTP POST request. /// /// URI endpoint to send request to - /// representing the received HTTP response + /// The returned Task Post(Uri uri); + /// + /// Performs an asynchronous HTTP POST request. + /// + /// URI endpoint to send request to + /// The object to serialize as the body of the request + /// Specifies accepted response media types. + /// The returned + Task Post(Uri uri, object body, string accepts); + /// /// Performs an asynchronous HTTP POST request. /// Attempts to map the response body to an object of type diff --git a/Octokit/Models/Request/NewProject.cs b/Octokit/Models/Request/NewProject.cs new file mode 100644 index 00000000..07d42cfa --- /dev/null +++ b/Octokit/Models/Request/NewProject.cs @@ -0,0 +1,36 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class NewProject + { + /// + /// Initializes a new instance of the class. + /// + /// The name of the project. + public NewProject(string name) + { + Name = name; + } + + /// + /// Required. Gets or sets the name of the project. + /// + public string Name { get; private set; } + + /// + /// Optional. Gets or sets the body of the project. + /// + public string Body { get; set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Name: {0}, Body: {1}", Name, Body); + } + } + } +} diff --git a/Octokit/Models/Request/NewProjectCard.cs b/Octokit/Models/Request/NewProjectCard.cs new file mode 100644 index 00000000..35c8cb28 --- /dev/null +++ b/Octokit/Models/Request/NewProjectCard.cs @@ -0,0 +1,52 @@ +using Octokit.Internal; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class NewProjectCard + { + public NewProjectCard(string note) + { + Note = note; + } + + public NewProjectCard(int contentId, ProjectCardContentType contentType) + { + ContentId = contentId; + ContentType = contentType; + } + + /// + /// The note of the card. + /// + public string Note { get; protected set; } + + /// + /// The id of the Issue or Pull Request to associate with this card. + /// + [Parameter(Key = "content_id")] + public int? ContentId { get; protected set; } + + /// + /// The type of content to associate with this card. + /// + [Parameter(Key = "content_type")] + public ProjectCardContentType? ContentType { get; protected set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Note: {0}, Id: {1}", Note, ContentId); + } + } + } + + public enum ProjectCardContentType + { + [Parameter(Value = "Issue")] + Issue + } +} diff --git a/Octokit/Models/Request/NewProjectColumn.cs b/Octokit/Models/Request/NewProjectColumn.cs new file mode 100644 index 00000000..cf4abf58 --- /dev/null +++ b/Octokit/Models/Request/NewProjectColumn.cs @@ -0,0 +1,27 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class NewProjectColumn + { + public NewProjectColumn(string name) + { + Name = name; + } + + /// + /// Required. Gets or sets the name of the column. + /// + public string Name { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Name: {0}", Name); + } + } + } +} diff --git a/Octokit/Models/Request/ProjectCardMove.cs b/Octokit/Models/Request/ProjectCardMove.cs new file mode 100644 index 00000000..90496e7a --- /dev/null +++ b/Octokit/Models/Request/ProjectCardMove.cs @@ -0,0 +1,46 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ProjectCardMove + { + public ProjectCardMove(ProjectCardPosition position, int columnId, int? cardId) + { + ColumnId = columnId; + switch (position) + { + case ProjectCardPosition.Top: + Position = "top"; + break; + case ProjectCardPosition.Bottom: + Position = "bottom"; + break; + case ProjectCardPosition.After: + Ensure.ArgumentNotNull(cardId, "cardId"); + Position = string.Format(CultureInfo.InvariantCulture, "after:{0}", cardId); + break; + } + } + + public string Position { get; private set; } + + public int ColumnId { get; set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Position: {0}", Position); + } + } + } + + public enum ProjectCardPosition + { + Top, + Bottom, + After + } +} diff --git a/Octokit/Models/Request/ProjectCardUpdate.cs b/Octokit/Models/Request/ProjectCardUpdate.cs new file mode 100644 index 00000000..e0fe7c1c --- /dev/null +++ b/Octokit/Models/Request/ProjectCardUpdate.cs @@ -0,0 +1,27 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ProjectCardUpdate + { + public ProjectCardUpdate(string note) + { + Note = note; + } + + /// + /// The new note of the card. + /// + public string Note { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Note: {0}", Note); + } + } + } +} diff --git a/Octokit/Models/Request/ProjectColumnMove.cs b/Octokit/Models/Request/ProjectColumnMove.cs new file mode 100644 index 00000000..23bddce8 --- /dev/null +++ b/Octokit/Models/Request/ProjectColumnMove.cs @@ -0,0 +1,43 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ProjectColumnMove + { + public ProjectColumnMove(ProjectColumnPosition position, int? columnId) + { + switch (position) + { + case ProjectColumnPosition.After: + Ensure.ArgumentNotNull(columnId, "columnId"); + Position = string.Format(CultureInfo.InvariantCulture, "after:{0}", columnId); + break; + case ProjectColumnPosition.First: + Position = "first"; + break; + case ProjectColumnPosition.Last: + Position = "last"; + break; + } + } + + public string Position { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Position: {0}", Position); + } + } + } + + public enum ProjectColumnPosition + { + First, + Last, + After + } +} diff --git a/Octokit/Models/Request/ProjectColumnUpdate.cs b/Octokit/Models/Request/ProjectColumnUpdate.cs new file mode 100644 index 00000000..b80d123e --- /dev/null +++ b/Octokit/Models/Request/ProjectColumnUpdate.cs @@ -0,0 +1,27 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ProjectColumnUpdate + { + public ProjectColumnUpdate(string name) + { + Name = name; + } + + /// + /// Required. Gets or sets the name of the column. + /// + public string Name { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Name: {0}", Name); + } + } + } +} diff --git a/Octokit/Models/Request/ProjectRequest.cs b/Octokit/Models/Request/ProjectRequest.cs new file mode 100644 index 00000000..dbf534a0 --- /dev/null +++ b/Octokit/Models/Request/ProjectRequest.cs @@ -0,0 +1,30 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + /// + /// Used to filter requests for lists of projects + /// + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ProjectRequest : RequestParameters + { + public ProjectRequest(ItemStateFilter state) + { + State = state; + } + + /// + /// Which projects to get. The default is . + /// + public ItemStateFilter State { get; private set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "State {0} ", State); + } + } + } +} diff --git a/Octokit/Models/Request/ProjectUpdate.cs b/Octokit/Models/Request/ProjectUpdate.cs new file mode 100644 index 00000000..55dd0bb8 --- /dev/null +++ b/Octokit/Models/Request/ProjectUpdate.cs @@ -0,0 +1,36 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ProjectUpdate + { + public ProjectUpdate() + { + } + + /// + /// The new name of the project. + /// + public string Name { get; set; } + + /// + /// The new body of the project. + /// + public string Body { get; set; } + + /// + /// The new state of the project. + /// + public ItemState? State { get; set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Name: {0}, Body: {1}", Name, Body); + } + } + } +} diff --git a/Octokit/Models/Response/Project.cs b/Octokit/Models/Response/Project.cs new file mode 100644 index 00000000..c548e1df --- /dev/null +++ b/Octokit/Models/Response/Project.cs @@ -0,0 +1,84 @@ +using System; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class Project + { + public Project() { } + + public Project(string ownerUrl, string url, int id, string name, string body, int number, ItemState state, User creator, DateTimeOffset createdAt, DateTimeOffset updatedAt) + { + OwnerUrl = ownerUrl; + Url = url; + Id = id; + Name = name; + Body = body; + Number = number; + State = state; + Creator = creator; + CreatedAt = createdAt; + UpdatedAt = updatedAt; + } + + /// + /// The URL for this projects repository. + /// + public string OwnerUrl { get; protected set; } + + /// + /// The URL for this project. + /// + public string Url { get; protected set; } + + /// + /// The Id for this project. + /// + public int Id { get; protected set; } + + /// + /// The name for this project. + /// + public string Name { get; protected set; } + + /// + /// The body for this project. + /// + public string Body { get; protected set; } + + /// + /// The number for this project. + /// + public int Number { get; protected set; } + + /// + /// The current state of this project. + /// + public StringEnum State { get; protected set; } + + /// + /// The user associated with this project. + /// + public User Creator { get; protected set; } + + /// + /// When this project was created. + /// + public DateTimeOffset CreatedAt { get; protected set; } + + /// + /// When this project was last updated. + /// + public DateTimeOffset UpdatedAt { get; protected set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Name: {0}, Number: {1}", Name, Number); + } + } + } +} diff --git a/Octokit/Models/Response/ProjectCard.cs b/Octokit/Models/Response/ProjectCard.cs new file mode 100644 index 00000000..8c313ec7 --- /dev/null +++ b/Octokit/Models/Response/ProjectCard.cs @@ -0,0 +1,66 @@ +using System; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ProjectCard + { + public ProjectCard() { } + + public ProjectCard(string columnUrl, string contentUrl, int id, string note, User creator, DateTimeOffset createdAt, DateTimeOffset updatedAt) + { + ColumnUrl = columnUrl; + ContentUrl = contentUrl; + Id = id; + Note = note; + Creator = creator; + CreatedAt = createdAt; + UpdatedAt = updatedAt; + } + + /// + /// The URL for this cards column. + /// + public string ColumnUrl { get; protected set; } + + /// + /// The URL for this cards content. + /// + public string ContentUrl { get; protected set; } + + /// + /// The Id for this card. + /// + public int Id { get; protected set; } + + /// + /// The note for this card. + /// + public string Note { get; protected set; } + + /// + /// The user associated with this card. + /// + public User Creator { get; protected set; } + + /// + /// When this card was created. + /// + public DateTimeOffset CreatedAt { get; protected set; } + + /// + /// When this card was last updated. + /// + public DateTimeOffset UpdatedAt { get; protected set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Note: {0}, Id: {1}", Note, Id); + } + } + } +} diff --git a/Octokit/Models/Response/ProjectColumn.cs b/Octokit/Models/Response/ProjectColumn.cs new file mode 100644 index 00000000..3b1de249 --- /dev/null +++ b/Octokit/Models/Response/ProjectColumn.cs @@ -0,0 +1,54 @@ +using System; +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ProjectColumn + { + public ProjectColumn() { } + + public ProjectColumn(int id, string name, string projectUrl, DateTimeOffset createdAt, DateTimeOffset updatedAt) + { + Id = id; + Name = name; + ProjectUrl = projectUrl; + CreatedAt = createdAt; + UpdatedAt = updatedAt; + } + + /// + /// The Id for this column. + /// + public int Id { get; protected set; } + + /// + /// The name for this column. + /// + public string Name { get; protected set; } + + /// + /// The URL for this columns project. + /// + public string ProjectUrl { get; protected set; } + + /// + /// When this column was created. + /// + public DateTimeOffset CreatedAt { get; protected set; } + + /// + /// When this column was last updated. + /// + public DateTimeOffset UpdatedAt { get; protected set; } + + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "Name: {0}, Id: {1}", Name, Id); + } + } + } +}