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