diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 22a32416..64a9bb71 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,66 +1,35 @@ # How to Contribute -We love Pull Requests! Your contributions help make Octokit great. +Contributions take many forms from submitting issues, writing docs, to making +code changes - we welcome it all! ## Getting Started -So you want to contribute to Octokit. Great! Contributions take many forms -from submitting issues, writing docs, to making code changes. We welcome -it all. Don't forget to sign up for a [GitHub account](https://github.com/signup/free), -if you haven't already. +If you don't have a GitHub account, you can [sign up](https://github.com/signup/free) +as it will help you to participate with the project. -## Getting Started +If you are looking to contribute to the codebase, please ensure you have Visual +Studio 2015 installed - you can download the Community edition from +[here](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx) -You can clone this repository locally from GitHub using the "Clone in Desktop" -button from the main project site, or run this command in the Git Shell: +If you are running GitHub Desktop, you can clone this repository locally from +GitHub using the "Clone in Desktop" button from the Octokit.net project page, +or run this command in your Git-enabled shell: -`git clone git@github.com:octokit/Octokit.net.git Octokit` +`git clone https://github.com/octokit/Octokit.net.git Octokit` -If you want to make contributions to the project, -[forking the project](https://help.github.com/articles/fork-a-repo) is the +If you want to make contributions to the project, +[forking the project](https://help.github.com/articles/fork-a-repo) is the easiest way to do this. You can then clone down your fork instead: -`git clone git@github.com:MY-USERNAME-HERE/Octokit.net.git Octokit` +`git clone https://github.com/MY-USERNAME-HERE/Octokit.net.git Octokit` -After doing that, run the `.\build.cmd` script at the root of the repository -to ensure all the tests pass. +After doing that, run the `.\build` script at the root of the repository +to ensure everything builds and the tests pass. -### How is the codebase organised? +## How can I get involved? -The two main projects are the `Octokit` and `Octokit.Reactive` projects. - -The `Octokit.Reactive` library is a thin wrapper over the `Octokit` -library - for those who want to use Reactive Extensions (Rx) instead of tasks. - -The namespaces are organised so that the relevant components are easy to discover: - - - **Authentication** - everything related to authenticating requests - - **Clients** - the logic for interacting with various parts of the GitHub API - - **Exceptions** - types which represent exceptional behaviour from the API - - **Helpers** - assorted extensions and helpers to keep the code neat and tidy - - **Http** - the internal networking components which Octokit requires - - **Models** - types which represent request/response objects - -Unless you're modifying some core behaviour, the **Clients** and **Models** namespaces -are likely to be the most interesting areas. - -The clients within a project are organized similarly to the endpoints in the -[GitHub API documentation](http://developer.github.com/v3/) - -Some clients are "sub-clients". For example, when you navigate to the -[Issues API](http://developer.github.com/v3/issues/) you'll notice there's an -endpoint for issues. But in the right navbar, there are other APIs such as -[Assignees](http://developer.github.com/v3/issues/assignees/) and -[Milestones](http://developer.github.com/v3/issues/milestones/). - -We've tried to mirror this structure. So the `IObservableMilestoneClient` isn't -a direct property of `IObservableGitHubClient`. Instead, it's a property of the -`IObservableIssuesClient`. And thus you can get to it by going to -`client.Issues.Milestones`. - -### What needs to be done? - -We have a [`easy-fix`](https://github.com/octokit/octokit.net/issues?labels=easy-fix&state=open) +We have an [`up-for-grabs`](https://github.com/octokit/octokit.net/issues/labels/up-for-grabs) tag on our issue tracker to indicate tasks which contributors can pick up. If you've found something you'd like to contribute to, leave a comment in the issue @@ -72,28 +41,63 @@ for ways to improve the API to make it easy to work with the GitHub API. ## Making Changes -When you're ready to make a change, -[create a branch](https://help.github.com/articles/fork-a-repo#create-branches) -off the `master` branch. We use `master` as the default branch for the -repository, and it holds the most recent contributions, so any changes you make -in master might cause conflicts down the track. +When you're ready to make a change, create a branch off the `master` branch: + +``` +git checkout master +git pull origin master +git checkout -b SOME-BRANCH-NAME +``` + +We use `master` as the default branch for the repository, and it holds the most +recent contributions. By working in a branch away from `master` you can handle +potential conflicts that may occur in the future. If you make focused commits (instead of one monolithic commit) and have descriptive commit messages, this will help speed up the review process. -If you're adding new files to the Octokit project, we have a helper script to -synchronize these changes with the Mono* projects in the solution. +### Adding New files -Just run this command: `.\build FixProjects` +To ensure new files are available in the various projects, we have a helper script +to synchronize these changes across all the projects in the solution. + +If you need to create new files: + + - add the file to the main `Octokit` project + - build the project (to ensure the `csproj` change is saved) + - run this command: `.\build FixProjects` + +At any time you can build the project with `.\build BuildApp` - this will also +run FxCop analysis. + +### Running Tests Octokit.net also has a suite of tests which you can run to ensure existing -behaviour is unchanged. If you're adding new features, please add some -tests alongside so the maintainers can sleep at night, knowing their +behaviour is not affected. If you're adding new features, please add some +tests alongside so the maintainers can sleep at night, knowing their safety blanket is nice and green! -Run this command to confirm all the tests pass: `.\build` +The test suite is arranged into fast and slow tests. -### Running integration tests +#### Fast Tests + +**Unit Tests:** `.\build UnitTests` + +These tests verify specific behaviour while being isolated from the rest of the +library. If you are not familiar with unit testing, have a look at the existing +examples - they should be easy to apply to your work. + +**Convention Tests:** `.\build ConventionTests` + +These tests verify conventions and structure across the entire codebase - +ensuring everything is consistent and predictable. When writing new features, +these tests may fail and should help indicate where the changes have violated +the conventions, so feel free to run them locally while you're working on new +features. + +#### Slow Tests + +**Integration Tests** Octokit has integration tests that access the GitHub API, but they require a bit of setup to run. The tests make use of a set of test accounts accessed via @@ -104,9 +108,23 @@ variables: `.\script\configure-integration-tests.ps1` -Once these are set, the integration tests will be executed both when -running the IntegrationTests build target, or when running the -Octokit.Tests.Integration assembly in the Visual Studio test runner. +After running this, ensure any existing instances of Visual Studio are restarted +so they pick up the new environment variables are detected. + +With these variables set, you can run the integration tests locally using +`.\build IntegrationTests` or by running the `Octokit.Tests.Integration` +assembly in the Visual Studio test runner. + +**Note:** as the integration tests rely on using the actual GitHub API, you may +encounter issues if running the tests too frequently. Please use a test account +so that you're not impacted in the unlikely scenario of your account being +flagged as a spammer. + +### Testing Documentation + +If you are making changes to the documentation for Octokit, you can test these +changes locally using the [guide](https://github.com/shiftkey/octokit.net/blob/rewrite-contributing/docs/contributing.md) +under the `docs` folder. ### Submitting Changes @@ -116,29 +134,34 @@ the Git Shell: `git push origin MY-BRANCH-NAME` Once your changes are ready to be reviewed, publish the branch to GitHub and -[open a pull request](https://help.github.com/articles/using-pull-requests) +[open a pull request](https://help.github.com/articles/using-pull-requests) against it. -A few little tips with pull requests: +A few suggestions when opening a pull request: + + - if you are addressing a particular issue, reference it like this: + +> Fixes #1145 - prefix the title with `[WIP]` to indicate this is a work-in-progress. It's - always good to get feedback early, so don't be afraid to open the PR before it's "done". - - use [checklists](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments) - to indicate the tasks which need to be done, so everyone knows how close you are to done. - - add comments to the PR about things that are unclear or you would like suggestions on + always good to get feedback early, so don't be afraid to open the PR before + it's "done". + - use [checklists](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments) + to indicate the tasks which need to be done, so everyone knows how close you + are to done. + - add comments to the PR about things that are unclear or you would like + suggestions on -Don't forget to mention in the pull request description which issue/issues are -being addressed. - -Some things that will increase the chance that your pull request is accepted. +Some things that will increase the chance that your pull request is accepted: * Follow existing code conventions. Most of what we do follows [standard .NET conventions](https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md) except in a few places. We include a ReSharper team settings file. -* Include unit tests that would otherwise fail without your code, but pass with +* Include unit tests that would otherwise fail without your code, but pass with it. -* Update the documentation, the surrounding one, examples elsewhere, guides, +* Update the documentation, the surrounding one, examples elsewhere, guides, whatever is affected by your contribution # Additional Resources +* [Octokit Codebase Overview](https://github.com/octokit/octokit.net/blob/master/OVERVIEW.md) * [General GitHub documentation](http://help.github.com/) diff --git a/OVERVIEW.md b/OVERVIEW.md new file mode 100644 index 00000000..8276639b --- /dev/null +++ b/OVERVIEW.md @@ -0,0 +1,32 @@ +### How is the codebase organised? + +The two main projects are the `Octokit` and `Octokit.Reactive` projects. + +The `Octokit.Reactive` library is a thin wrapper over the `Octokit` +library - for those who want to use Reactive Extensions (Rx) instead of tasks. + +The namespaces are organised so that the relevant components are easy to discover: + + - **Authentication** - everything related to authenticating requests + - **Clients** - the logic for interacting with various parts of the GitHub API + - **Exceptions** - types which represent exceptional behaviour from the API + - **Helpers** - assorted extensions and helpers to keep the code neat and tidy + - **Http** - the internal networking components which Octokit requires + - **Models** - types which represent request/response objects + +Unless you're modifying some core behaviour, the **Clients** and **Models** namespaces +are likely to be the most interesting areas. + +The clients within a project are organized similarly to the endpoints in the +[GitHub API documentation](http://developer.github.com/v3/) + +Some clients are "sub-clients". For example, when you navigate to the +[Issues API](http://developer.github.com/v3/issues/) you'll notice there's an +endpoint for issues. But in the right navbar, there are other APIs such as +[Assignees](http://developer.github.com/v3/issues/assignees/) and +[Milestones](http://developer.github.com/v3/issues/milestones/). + +We've tried to mirror this structure. So the `IObservableMilestoneClient` isn't +a direct property of `IObservableGitHubClient`. Instead, it's a property of the +`IObservableIssuesClient`. And thus you can get to it by going to +`client.Issues.Milestones`. diff --git a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs index d5f4bd1f..1791d57e 100644 --- a/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/IObservableEnterpriseLdapClient.cs @@ -33,7 +33,7 @@ namespace Octokit.Reactive /// The username to sync LDAP mapping /// The of the queue request. IObservable QueueSyncUserMapping(string userName); - + /// /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). /// diff --git a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs index f096e496..0a037406 100644 --- a/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs +++ b/Octokit.Reactive/Clients/Enterprise/ObservableEnterpriseLdapClient.cs @@ -49,7 +49,7 @@ namespace Octokit.Reactive { return _client.QueueSyncUserMapping(userName).ToObservable(); } - + /// /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). /// diff --git a/Octokit.Reactive/Clients/IObservableAssigneesClient.cs b/Octokit.Reactive/Clients/IObservableAssigneesClient.cs index b4be296d..b47ad18e 100644 --- a/Octokit.Reactive/Clients/IObservableAssigneesClient.cs +++ b/Octokit.Reactive/Clients/IObservableAssigneesClient.cs @@ -12,6 +12,15 @@ namespace Octokit.Reactive /// IObservable GetAllForRepository(string owner, string name); + /// + /// Gets all the available assignees (owner + collaborators) to which issues may be assigned. + /// + /// The owner of the repository + /// The name of the repository + /// The options to change API's behaviour. + /// + IObservable GetAllForRepository(string owner, string name, ApiOptions options); + /// /// Checks to see if a user is an assignee for a repository. /// diff --git a/Octokit.Reactive/Clients/IObservableAuthorizationsClient.cs b/Octokit.Reactive/Clients/IObservableAuthorizationsClient.cs index c72986fd..0e5083fd 100644 --- a/Octokit.Reactive/Clients/IObservableAuthorizationsClient.cs +++ b/Octokit.Reactive/Clients/IObservableAuthorizationsClient.cs @@ -13,11 +13,24 @@ namespace Octokit.Reactive /// See API documentation for more /// details. /// - /// An + /// A list of s for the authenticated user. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "It's an API call, so it's not a property.")] IObservable GetAll(); + /// + /// Get all s for the authenticated user. This method requires basic auth. + /// + /// + /// See API documentation for more + /// details. + /// + /// Options for changing the API response + /// A list of s for the authenticated user. + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification = "It's an API call, so it's not a property.")] + IObservable GetAll(ApiOptions options); + /// /// Get a specific for the authenticated user. This method requires basic auth. /// diff --git a/Octokit.Reactive/Clients/IObservableReleasesClient.cs b/Octokit.Reactive/Clients/IObservableReleasesClient.cs index a8bfa4cc..242e13d2 100644 --- a/Octokit.Reactive/Clients/IObservableReleasesClient.cs +++ b/Octokit.Reactive/Clients/IObservableReleasesClient.cs @@ -18,6 +18,19 @@ namespace Octokit.Reactive /// The list of s for the specified repository. IObservable GetAll(string owner, string name); + /// + /// Gets all s for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The repository's owner + /// The repository's name + /// Options for changing the API response + /// Thrown when a general API error occurs. + /// The list of s for the specified repository. + IObservable GetAll(string owner, string name, ApiOptions options); + /// /// Gets a single for the specified repository. /// diff --git a/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs b/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs index 493e90b8..123860bc 100644 --- a/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs +++ b/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs @@ -328,7 +328,7 @@ namespace Octokit.Reactive /// Access GitHub's Releases API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/repos/releases/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/releases/ /// IObservableReleasesClient Release { get; } diff --git a/Octokit.Reactive/Clients/IObservableRepositoryCommitsClients.cs b/Octokit.Reactive/Clients/IObservableRepositoryCommitsClients.cs index 927b34c0..29d4d55e 100644 --- a/Octokit.Reactive/Clients/IObservableRepositoryCommitsClients.cs +++ b/Octokit.Reactive/Clients/IObservableRepositoryCommitsClients.cs @@ -43,5 +43,14 @@ namespace Octokit.Reactive /// Used to filter list of commits returned /// IObservable GetAll(string owner, string name, CommitRequest request); + + /// + /// Get the SHA-1 of a commit reference + /// + /// The owner of the repository + /// The name of the repository + /// The repository reference + /// + IObservable GetSha1(string owner, string name, string reference); } } diff --git a/Octokit.Reactive/Clients/IObservableUserEmailsClient.cs b/Octokit.Reactive/Clients/IObservableUserEmailsClient.cs index 5198ef1a..dd1c99c1 100644 --- a/Octokit.Reactive/Clients/IObservableUserEmailsClient.cs +++ b/Octokit.Reactive/Clients/IObservableUserEmailsClient.cs @@ -22,6 +22,17 @@ namespace Octokit.Reactive [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] IObservable GetAll(); + /// + /// Gets all email addresses for the authenticated user. + /// + /// + /// http://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user + /// + /// Options for changing the API response + /// The es for the authenticated user. + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + IObservable GetAll(ApiOptions options); + /// /// Adds email addresses for the authenticated user. /// diff --git a/Octokit.Reactive/Clients/ObservableAssigneesClient.cs b/Octokit.Reactive/Clients/ObservableAssigneesClient.cs index 63fe37d7..beabc06d 100644 --- a/Octokit.Reactive/Clients/ObservableAssigneesClient.cs +++ b/Octokit.Reactive/Clients/ObservableAssigneesClient.cs @@ -24,11 +24,24 @@ namespace Octokit.Reactive /// The name of the repository /// public IObservable GetAllForRepository(string owner, string name) + { + return GetAllForRepository(owner, name, ApiOptions.None); + } + + /// + /// Gets all the available assignees (owner + collaborators) to which issues may be assigned. + /// + /// The owner of the repository + /// The name of the repository + /// The options to change API's behaviour + /// + public IObservable GetAllForRepository(string owner, string name, ApiOptions options) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); - return _connection.GetAndFlattenAllPages(ApiUrls.Assignees(owner, name)); + return _connection.GetAndFlattenAllPages(ApiUrls.Assignees(owner, name), options); } /// diff --git a/Octokit.Reactive/Clients/ObservableAuthorizationsClient.cs b/Octokit.Reactive/Clients/ObservableAuthorizationsClient.cs index 9d5127b7..d5cd1989 100644 --- a/Octokit.Reactive/Clients/ObservableAuthorizationsClient.cs +++ b/Octokit.Reactive/Clients/ObservableAuthorizationsClient.cs @@ -25,10 +25,26 @@ namespace Octokit.Reactive /// See API documentation for more /// details. /// - /// An + /// A list of s for the authenticated user. public IObservable GetAll() { - return _connection.GetAndFlattenAllPages(ApiUrls.Authorizations()); + return GetAll(ApiOptions.None); + } + + /// + /// Get all s for the authenticated user. This method requires basic auth. + /// + /// + /// See API documentation for more + /// details. + /// + /// Options for changing the API response + /// A list of s for the authenticated user. + public IObservable GetAll(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Authorizations(), options); } /// diff --git a/Octokit.Reactive/Clients/ObservableReleasesClient.cs b/Octokit.Reactive/Clients/ObservableReleasesClient.cs index dfc9478e..100bd131 100644 --- a/Octokit.Reactive/Clients/ObservableReleasesClient.cs +++ b/Octokit.Reactive/Clients/ObservableReleasesClient.cs @@ -36,6 +36,26 @@ namespace Octokit.Reactive return _connection.GetAndFlattenAllPages(ApiUrls.Releases(owner, name)); } + /// + /// Gets all s for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The repository's owner + /// The repository's name + /// Options for changing the API response + /// Thrown when a general API error occurs. + /// The list of s for the specified repository. + public IObservable GetAll(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Releases(owner, name), options); + } + /// /// Gets a single for the specified repository. /// diff --git a/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs b/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs index 1495afb7..005270bb 100644 --- a/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs +++ b/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs @@ -475,7 +475,7 @@ namespace Octokit.Reactive /// Access GitHub's Releases API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/repos/releases/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/releases/ /// public IObservableReleasesClient Release { get; private set; } diff --git a/Octokit.Reactive/Clients/ObservableRepositoryCommitsClients.cs b/Octokit.Reactive/Clients/ObservableRepositoryCommitsClients.cs index 7982485e..629142d7 100644 --- a/Octokit.Reactive/Clients/ObservableRepositoryCommitsClients.cs +++ b/Octokit.Reactive/Clients/ObservableRepositoryCommitsClients.cs @@ -73,5 +73,21 @@ namespace Octokit.Reactive return _connection.GetAndFlattenAllPages(ApiUrls.RepositoryCommits(owner, name), request.ToParametersDictionary()); } + + /// + /// Get the SHA-1 of a commit reference + /// + /// The owner of the repository + /// The name of the repository + /// The repository reference + /// + public IObservable GetSha1(string owner, string name, string reference) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(reference, "reference"); + + return _commit.GetSha1(owner, name, reference).ToObservable(); + } } } diff --git a/Octokit.Reactive/Clients/ObservableUserEmailsClient.cs b/Octokit.Reactive/Clients/ObservableUserEmailsClient.cs index 2a2d1071..96076c30 100644 --- a/Octokit.Reactive/Clients/ObservableUserEmailsClient.cs +++ b/Octokit.Reactive/Clients/ObservableUserEmailsClient.cs @@ -34,7 +34,22 @@ namespace Octokit.Reactive /// The es for the authenticated user. public IObservable GetAll() { - return _connection.GetAndFlattenAllPages(ApiUrls.Emails()); + return GetAll(ApiOptions.None); + } + + /// + /// Gets all email addresses for the authenticated user. + /// + /// + /// http://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user + /// + /// Options for changing the API response + /// The es for the authenticated user. + public IObservable GetAll(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.Emails(), options); } /// diff --git a/Octokit.Reactive/Helpers/ConnectionExtensions.cs b/Octokit.Reactive/Helpers/ConnectionExtensions.cs index 0094134c..becbf01c 100644 --- a/Octokit.Reactive/Helpers/ConnectionExtensions.cs +++ b/Octokit.Reactive/Helpers/ConnectionExtensions.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.Linq; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; @@ -12,6 +14,15 @@ namespace Octokit.Reactive.Internal return GetPages(url, null, (pageUrl, pageParams) => connection.Get>(pageUrl, null, null).ToObservable()); } + public static IObservable GetAndFlattenAllPages(this IConnection connection, Uri url, ApiOptions options) + { + return GetPagesWithOptions(url, options, (pageUrl, o) => + { + var parameters = Pagination.Setup(new Dictionary(), options); + return connection.Get>(pageUrl, parameters, null).ToObservable(); + }); + } + public static IObservable GetAndFlattenAllPages(this IConnection connection, Uri url, IDictionary parameters) { return GetPages(url, parameters, (pageUrl, pageParams) => connection.Get>(pageUrl, pageParams, null).ToObservable()); @@ -35,5 +46,24 @@ namespace Octokit.Reactive.Internal .Where(resp => resp != null) .SelectMany(resp => resp.Body); } + + static IObservable GetPagesWithOptions(Uri uri, ApiOptions options, + Func>>> getPageFunc) + { + return getPageFunc(uri, options).Expand(resp => + { + var nextPageUri = resp.HttpResponse.ApiInfo.GetNextPageUrl(); + + var shouldContinue = Pagination.ShouldContinue( + nextPageUri, + options); + + return shouldContinue + ? Observable.Defer(() => getPageFunc(nextPageUri, null)) + : Observable.Empty>>(); + }) + .Where(resp => resp != null) + .SelectMany(resp => resp.Body); + } } } diff --git a/Octokit.Reactive/Octokit.Reactive-Mono.csproj b/Octokit.Reactive/Octokit.Reactive-Mono.csproj index 41db9810..6cf3a026 100644 --- a/Octokit.Reactive/Octokit.Reactive-Mono.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Mono.csproj @@ -52,6 +52,9 @@ Helpers\Ensure.cs + + Helpers\Pagination.cs + Properties\SolutionInfo.cs @@ -181,4 +184,4 @@ Octokit-Mono - \ No newline at end of file + diff --git a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj index 2a01d4da..146e66b0 100644 --- a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj +++ b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj @@ -60,6 +60,9 @@ Helpers\Ensure.cs + + Helpers\Pagination.cs + Properties\SolutionInfo.cs @@ -189,4 +192,4 @@ Octokit-MonoAndroid - \ No newline at end of file + diff --git a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj index c3855b70..c3d64929 100644 --- a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj @@ -56,6 +56,9 @@ Helpers\Ensure.cs + + Helpers\Pagination.cs + Properties\SolutionInfo.cs @@ -185,4 +188,4 @@ Octokit-Monotouch - \ No newline at end of file + diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj index 4c69b57f..8d1d8e99 100644 --- a/Octokit.Reactive/Octokit.Reactive.csproj +++ b/Octokit.Reactive/Octokit.Reactive.csproj @@ -72,6 +72,9 @@ Helpers\Ensure.cs + + Helpers\Pagination.cs + Properties\SolutionInfo.cs diff --git a/Octokit.Tests.Conventions/Exception/ApiOptionsMissingException.cs b/Octokit.Tests.Conventions/Exception/ApiOptionsMissingException.cs new file mode 100644 index 00000000..61d1111f --- /dev/null +++ b/Octokit.Tests.Conventions/Exception/ApiOptionsMissingException.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Octokit.Tests.Conventions +{ + public class ApiOptionsMissingException : Exception + { + public ApiOptionsMissingException(Type type, IEnumerable methods) + : base(CreateMessage(type, methods)) { } + + static string CreateMessage(Type type, IEnumerable methods) + { + var methodsFormatted = String.Join("\r\n", methods.Select(FormatMethod)); + return "Methods found on type {0} require an overload which accepts an parameter of type ApiOptions:\r\n{1}" + .FormatWithNewLine( + type.Name, + methodsFormatted); + } + + static string FormatMethod(MethodInfo m) + { + var formattedParameters = m.GetParameters() + .Select(p => string.Format("{0} {1}", p.ParameterType.Name, p.Name)); + + var parameterList = string.Join(", ", formattedParameters); + + return string.Format(" - {0}({1})", m.Name, parameterList); + } + } +} diff --git a/Octokit.Tests.Conventions/ModelTests.cs b/Octokit.Tests.Conventions/ModelTests.cs index f126e8cd..0cfcb3c8 100644 --- a/Octokit.Tests.Conventions/ModelTests.cs +++ b/Octokit.Tests.Conventions/ModelTests.cs @@ -95,27 +95,6 @@ namespace Octokit.Tests.Conventions } } - //TODO: This should (probably) be moved to the PaginationTests class that is being introduced in PR #760 - [Theory] - [MemberData("GetClientInterfaces")] - public void CheckPaginationGetAllMethodNames(Type clientInterface) - { - var methodsOrdered = clientInterface.GetMethodsOrdered(); - - var methodsThatCanPaginate = methodsOrdered - .Where(x => x.ReturnType.GetTypeInfo().TypeCategory == TypeCategory.ReadOnlyList) - .Where(x => x.Name.StartsWith("Get")); - - var invalidMethods = methodsThatCanPaginate - .Where(x => !x.Name.StartsWith("GetAll")) - .ToList(); - - if (invalidMethods.Any()) - { - throw new PaginationGetAllMethodNameMismatchException(clientInterface, invalidMethods); - } - } - public static IEnumerable GetClientInterfaces() { return typeof(IEventsClient) diff --git a/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj b/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj index fe11b292..c6012156 100644 --- a/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj +++ b/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj @@ -68,6 +68,7 @@ + @@ -76,6 +77,7 @@ + diff --git a/Octokit.Tests.Conventions/PaginationTests.cs b/Octokit.Tests.Conventions/PaginationTests.cs new file mode 100644 index 00000000..3c2565e7 --- /dev/null +++ b/Octokit.Tests.Conventions/PaginationTests.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Xunit; + +namespace Octokit.Tests.Conventions +{ + public class PaginationTests + { + [Theory(Skip = "Enable this to run it and find all the places where things break")] + [MemberData("GetClientInterfaces")] + public void CheckObservableClients(Type clientInterface) + { + var methodsOrdered = clientInterface.GetMethodsOrdered(); + + var methodsWhichCanPaginate = methodsOrdered + .Where(x => x.Name.StartsWith("GetAll")); + + var invalidMethods = methodsWhichCanPaginate + .Where(method => MethodHasAppropriateOverload(method, methodsOrdered) == null) + .ToList(); + + if (invalidMethods.Any()) + { + throw new ApiOptionsMissingException(clientInterface, invalidMethods); + } + } + + [Theory] + [MemberData("GetClientInterfaces")] + public void CheckPaginationGetAllMethodNames(Type clientInterface) + { + var methodsOrdered = clientInterface.GetMethodsOrdered(); + + var methodsThatCanPaginate = methodsOrdered + .Where(x => x.ReturnType.GetTypeInfo().TypeCategory == TypeCategory.ReadOnlyList) + .Where(x => x.Name.StartsWith("Get")); + + var invalidMethods = methodsThatCanPaginate + .Where(x => !x.Name.StartsWith("GetAll")) + .ToList(); + + if (invalidMethods.Any()) + { + throw new PaginationGetAllMethodNameMismatchException(clientInterface, invalidMethods); + } + } + + static MethodInfo MethodHasAppropriateOverload(MethodInfo method, MethodInfo[] methodsOrdered) + { + var parameters = method.GetParametersOrdered(); + var name = method.Name; + return methodsOrdered + .Where(x => x.Name == name) + .FirstOrDefault(x => MethodHasOverloadForApiOptions(x, parameters)); + } + + static bool MethodHasOverloadForApiOptions(MethodInfo methodInfo, ParameterInfo[] expected) + { + var actual = methodInfo.GetParameters(); + + if (actual.Length != expected.Length + 1) + { + return false; + } + + for (var i = 0; i < expected.Length; i++) + { + var a = actual.ElementAt(i); + var e = expected.ElementAt(i); + + if (a.Name != e.Name) + { + return false; + } + + if (a.ParameterType != e.ParameterType) + { + return false; + } + } + + var lastParameter = actual.Last(); + + return lastParameter.Name == "options" + && lastParameter.ParameterType == typeof(ApiOptions); + } + + public static IEnumerable GetClientInterfaces() + { + return typeof(IEventsClient).Assembly.ExportedTypes + .Where(TypeExtensions.IsClientInterface) + .Where(type => type != typeof(IStatisticsClient)) + .Select(type => new[] { type }); + } + } +} diff --git a/Octokit.Tests.Integration/Clients/AuthorizationClientTests.cs b/Octokit.Tests.Integration/Clients/AuthorizationClientTests.cs index 3108c42b..994c29b5 100644 --- a/Octokit.Tests.Integration/Clients/AuthorizationClientTests.cs +++ b/Octokit.Tests.Integration/Clients/AuthorizationClientTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Xunit; namespace Octokit.Tests.Integration.Clients @@ -13,7 +12,7 @@ namespace Octokit.Tests.Integration.Clients var note = Helper.MakeNameWithTimestamp("Testing authentication"); var newAuthorization = new NewAuthorization( note, - new string[] { "user" }); + new[] { "user" }); var created = await github.Authorization.Create(newAuthorization); @@ -27,6 +26,39 @@ namespace Octokit.Tests.Integration.Clients Assert.Equal(created.Note, get.Note); } + [IntegrationTest] + public async Task CanGetAuthorization() + { + var github = Helper.GetBasicAuthClient(); + + var authorizations = await github.Authorization.GetAll(); + Assert.NotEmpty(authorizations); + } + + [IntegrationTest] + public async Task CanGetAuthorizationWithApiOptions() + { + var github = Helper.GetBasicAuthClient(); + + var authorizations = await github.Authorization.GetAll(ApiOptions.None); + Assert.NotEmpty(authorizations); + } + + [IntegrationTest] + public async Task ReturnsNotEmptyAuthorizationsWithoutStart() + { + var github = Helper.GetBasicAuthClient(); + + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var authorizations = await github.Authorization.GetAll(options); + Assert.NotEmpty(authorizations); + } + [IntegrationTest] public async Task CannotCreatePersonalTokenWhenUsingOauthTokenCredentials() { @@ -34,7 +66,7 @@ namespace Octokit.Tests.Integration.Clients var note = Helper.MakeNameWithTimestamp("Testing authentication"); var newAuthorization = new NewAuthorization( note, - new string[] { "user" }); + new[] { "user" }); var error = Assert.ThrowsAsync(() => github.Authorization.Create(newAuthorization)); Assert.True(error.Result.Message.Contains("username and password Basic Auth")); diff --git a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs index edbca496..5fbac372 100644 --- a/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs +++ b/Octokit.Tests.Integration/Clients/Enterprise/EnterpriseLdapClientTests.cs @@ -19,7 +19,7 @@ public class EnterpriseLdapClientTests : IDisposable public EnterpriseLdapClientTests() { _github = EnterpriseHelper.GetAuthenticatedClient(); - + NewTeam newTeam = new NewTeam(Helper.MakeNameWithTimestamp("test-team")) { Description = "Test Team" }; _context = _github.CreateEnterpriseTeamContext(EnterpriseHelper.Organization, newTeam).Result; } diff --git a/Octokit.Tests.Integration/Clients/ReleasesClientTests.cs b/Octokit.Tests.Integration/Clients/ReleasesClientTests.cs index 0dd4df70..34892219 100644 --- a/Octokit.Tests.Integration/Clients/ReleasesClientTests.cs +++ b/Octokit.Tests.Integration/Clients/ReleasesClientTests.cs @@ -75,6 +75,83 @@ public class ReleasesClientTests } } + public class TheGetAllMethod + { + readonly IReleasesClient _releaseClient; + const string owner = "octokit"; + const string name = "octokit.net"; + + public TheGetAllMethod() + { + var github = Helper.GetAuthenticatedClient(); + _releaseClient = github.Repository.Release; + } + + [IntegrationTest] + public async Task ReturnsReleases() + { + var releases = await _releaseClient.GetAll(owner, name); + + Assert.NotEmpty(releases); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfReleasesWithoutStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var releases = await _releaseClient.GetAll(owner, name, options); + + Assert.Equal(5, releases.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfReleasesWithStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 2 + }; + + var releases = await _releaseClient.GetAll(owner, name, options); + + Assert.Equal(5, releases.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var firstPage = await _releaseClient.GetAll(owner, name, startOptions); + + var skipStartOptions = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _releaseClient.GetAll(owner, name, skipStartOptions); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + Assert.NotEqual(firstPage[1].Id, secondPage[1].Id); + Assert.NotEqual(firstPage[2].Id, secondPage[2].Id); + Assert.NotEqual(firstPage[3].Id, secondPage[3].Id); + Assert.NotEqual(firstPage[4].Id, secondPage[4].Id); + } + } + public class TheEditMethod : IDisposable { private readonly IGitHubClient _github; diff --git a/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs index 78247ebc..ebd29eb2 100644 --- a/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs +++ b/Octokit.Tests.Integration/Clients/RepositoriesClientTests.cs @@ -447,7 +447,7 @@ public class RepositoriesClientTests var repoName = Helper.MakeNameWithTimestamp("repo-to-delete"); await github.Repository.Create(new NewRepository(repoName)); - + await github.Repository.Delete(Helper.UserName, repoName); } } diff --git a/Octokit.Tests.Integration/Clients/RepositoryCommitsClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoryCommitsClientTests.cs index 95464917..74c4e43e 100644 --- a/Octokit.Tests.Integration/Clients/RepositoryCommitsClientTests.cs +++ b/Octokit.Tests.Integration/Clients/RepositoryCommitsClientTests.cs @@ -87,6 +87,14 @@ public class RepositoryCommitsClientTests .Where(file => file.Status == "renamed") .All(file => string.IsNullOrEmpty(file.PreviousFileName) == false)); } + + [IntegrationTest] + public async Task CanGetSha1() + { + var sha1 = await _fixture.GetSha1("octokit", "octokit.net", "master"); + + Assert.NotNull(sha1); + } } public class TestsWithNewRepository : IDisposable @@ -158,7 +166,17 @@ public class RepositoryCommitsClientTests Assert.Equal(0, result.BehindBy); } - async Task CreateTheWorld() + [IntegrationTest] + public async Task GetSha1FromRepository() + { + var reference = await CreateTheWorld(); + + var sha1 = await _fixture.GetSha1(Helper.UserName, _context.RepositoryName, "master"); + + Assert.Equal(reference.Object.Sha, sha1); + } + + async Task CreateTheWorld() { var master = await _github.Git.Reference.Get(Helper.UserName, _context.RepositoryName, "heads/master"); @@ -174,7 +192,7 @@ public class RepositoryCommitsClientTests var newFeature = await CreateCommit("this is the commit to merge into the pull request", featureBranchTree.Sha, newMaster.Sha); // create branch - await _github.Git.Reference.Create(Helper.UserName, _context.RepositoryName, new NewReference("refs/heads/my-branch", newFeature.Sha)); + return await _github.Git.Reference.Create(Helper.UserName, _context.RepositoryName, new NewReference("refs/heads/my-branch", newFeature.Sha)); } async Task CreateTree(IDictionary treeContents) diff --git a/Octokit.Tests.Integration/Clients/UserAdministrationClientTests.cs b/Octokit.Tests.Integration/Clients/UserAdministrationClientTests.cs index 321017ac..b9c049ab 100644 --- a/Octokit.Tests.Integration/Clients/UserAdministrationClientTests.cs +++ b/Octokit.Tests.Integration/Clients/UserAdministrationClientTests.cs @@ -95,7 +95,7 @@ namespace Octokit.Tests.Integration.Clients Assert.NotNull(token); Assert.True( - token.Scopes.Count() == 1 && + token.Scopes.Count() == 1 && token.Scopes.All(s => s == "public_repo")); // Delete Impersonation token @@ -187,7 +187,7 @@ namespace Octokit.Tests.Integration.Clients { // Ensure user has a key //var key = await _github.User.Keys.Create(new NewPublicKey("title", "key")); - + // Delete key //await _github.User.Administration.DeletePublicKey(key.Id); } diff --git a/Octokit.Tests.Integration/Clients/UserEmailsClientTests.cs b/Octokit.Tests.Integration/Clients/UserEmailsClientTests.cs index f0af752c..12ac23e4 100644 --- a/Octokit.Tests.Integration/Clients/UserEmailsClientTests.cs +++ b/Octokit.Tests.Integration/Clients/UserEmailsClientTests.cs @@ -6,15 +6,42 @@ namespace Octokit.Tests.Integration.Clients { public class UserEmailsClientTests { + private readonly IUserEmailsClient _emailClient; + + public UserEmailsClientTests() + { + var github = Helper.GetAuthenticatedClient(); + _emailClient = github.User.Email; + } + [IntegrationTest] public async Task CanGetEmail() { - var github = Helper.GetAuthenticatedClient(); - - var emails = await github.User.Email.GetAll(); + var emails = await _emailClient.GetAll(); Assert.NotEmpty(emails); } + [IntegrationTest] + public async Task CanGetEmailWithApiOptions() + { + var emails = await _emailClient.GetAll(ApiOptions.None); + Assert.NotEmpty(emails); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfEmailsWithoutStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var emails = await _emailClient.GetAll(options); + + Assert.NotEmpty(emails); + } + const string testEmailAddress = "hahaha-not-a-real-email@foo.com"; [IntegrationTest(Skip = "this isn't passing in CI - i hate past me right now")] diff --git a/Octokit.Tests.Integration/EnterpriseHelper.cs b/Octokit.Tests.Integration/EnterpriseHelper.cs index 7e03a604..baf16c9a 100644 --- a/Octokit.Tests.Integration/EnterpriseHelper.cs +++ b/Octokit.Tests.Integration/EnterpriseHelper.cs @@ -55,7 +55,7 @@ namespace Octokit.Tests.Integration string enabled = Environment.GetEnvironmentVariable("OCTOKIT_GHE_ENABLED"); return !String.IsNullOrWhiteSpace(enabled); }); - + static readonly Lazy _gitHubEnterpriseUrl = new Lazy(() => { string uri = Environment.GetEnvironmentVariable("OCTOKIT_GHE_URL"); @@ -76,7 +76,7 @@ namespace Octokit.Tests.Integration public static string UserName { get; private set; } public static string Organization { get; private set; } - + /// /// These credentials should be set to a test GitHub account using the powershell script configure-integration-tests.ps1 /// @@ -88,8 +88,8 @@ namespace Octokit.Tests.Integration public static bool IsGitHubEnterpriseEnabled { get { return _gitHubEnterpriseEnabled.Value; } } - public static Uri GitHubEnterpriseUrl { get { return _gitHubEnterpriseUrl.Value; } } - + public static Uri GitHubEnterpriseUrl { get { return _gitHubEnterpriseUrl.Value; } } + public static bool IsUsingToken { get diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 93cf79b3..4181ab35 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -137,8 +137,11 @@ + + + diff --git a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs index dde932d8..948a52a6 100644 --- a/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -14,10 +14,10 @@ namespace Octokit.Tests.Integration readonly string _testUser = "test-user"; readonly string _distinguishedNameUser = "uid=test-user,ou=users,dc=company,dc=com"; - + readonly EnterpriseTeamContext _context; readonly string _distinguishedNameTeam = "cn=test-team,ou=groups,dc=company,dc=com"; - + public ObservableEnterpriseLdapClientTests() { _github = new ObservableGitHubClient(EnterpriseHelper.GetAuthenticatedClient()); diff --git a/Octokit.Tests.Integration/Reactive/ObservableAssigneesClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableAssigneesClientTests.cs new file mode 100644 index 00000000..a85f326b --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableAssigneesClientTests.cs @@ -0,0 +1,88 @@ +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Integration.Reactive +{ + public class ObservableAssigneesClientTests + { + public class TheGetAllMethod + { + readonly ObservableAssigneesClient _assigneesClient; + const string owner = "octokit"; + const string name = "octokit.net"; + + public TheGetAllMethod() + { + var github = Helper.GetAuthenticatedClient(); + + _assigneesClient = new ObservableAssigneesClient(github); + } + + [IntegrationTest] + public async Task ReturnsAssignees() + { + var assignees = await _assigneesClient.GetAllForRepository(owner, name).ToList(); + + Assert.NotEmpty(assignees); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfAssigneesWithoutStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var assignees = await _assigneesClient.GetAllForRepository(owner, name, options).ToList(); + + Assert.Equal(5, assignees.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfAssigneesWithStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 2 + }; + + var releases = await _assigneesClient.GetAllForRepository(owner, name, options).ToList(); + + Assert.Equal(5, releases.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var firstPage = await _assigneesClient.GetAllForRepository(owner, name, startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _assigneesClient.GetAllForRepository(owner, name, skipStartOptions).ToList(); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + Assert.NotEqual(firstPage[1].Id, secondPage[1].Id); + Assert.NotEqual(firstPage[2].Id, secondPage[2].Id); + Assert.NotEqual(firstPage[3].Id, secondPage[3].Id); + Assert.NotEqual(firstPage[4].Id, secondPage[4].Id); + } + } + } +} \ No newline at end of file diff --git a/Octokit.Tests.Integration/Reactive/ObservableAuthorizationsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableAuthorizationsClientTests.cs new file mode 100644 index 00000000..348eb774 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableAuthorizationsClientTests.cs @@ -0,0 +1,46 @@ +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Integration.Reactive +{ + public class ObservableAuthorizationsClientTests + { + readonly ObservableAuthorizationsClient _authorizationsClient; + + public ObservableAuthorizationsClientTests() + { + var github = Helper.GetBasicAuthClient(); + + _authorizationsClient = new ObservableAuthorizationsClient(github); + } + + [IntegrationTest] + public async Task CanGetAuthorization() + { + var authorization = await _authorizationsClient.GetAll(); + Assert.NotNull(authorization); + } + + [IntegrationTest] + public async Task CanGetAuthorizationWithApiOptions() + { + var authorization = await _authorizationsClient.GetAll(ApiOptions.None); + Assert.NotNull(authorization); + } + + [IntegrationTest] + public async Task ReturnsNotEmptyAuthorizationsWithoutStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var authorizations = await _authorizationsClient.GetAll(options).ToList(); + Assert.NotEmpty(authorizations); + } + } +} diff --git a/Octokit.Tests.Integration/Reactive/ObservableReleaseClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableReleaseClientTests.cs new file mode 100644 index 00000000..337f2894 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableReleaseClientTests.cs @@ -0,0 +1,88 @@ +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Integration.Reactive +{ + public class ObservableReleaseClientTests + { + public class TheGetAllMethod + { + readonly ObservableReleasesClient _releaseClient; + const string owner = "octokit"; + const string name = "octokit.net"; + + public TheGetAllMethod() + { + var github = Helper.GetAuthenticatedClient(); + + _releaseClient = new ObservableReleasesClient(github); + } + + [IntegrationTest] + public async Task ReturnsReleases() + { + var releases = await _releaseClient.GetAll(owner, name).ToList(); + + Assert.NotEmpty(releases); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfReleasesWithoutStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var releases = await _releaseClient.GetAll(owner, name, options).ToList(); + + Assert.Equal(5, releases.Count); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfReleasesWithStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 2 + }; + + var releases = await _releaseClient.GetAll(owner, name, options).ToList(); + + Assert.Equal(5, releases.Count); + } + + [IntegrationTest] + public async Task ReturnsDistinctResultsBasedOnStartPage() + { + var startOptions = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var firstPage = await _releaseClient.GetAll(owner, name, startOptions).ToList(); + + var skipStartOptions = new ApiOptions + { + PageSize = 5, + PageCount = 1, + StartPage = 2 + }; + + var secondPage = await _releaseClient.GetAll(owner, name, skipStartOptions).ToList(); + + Assert.NotEqual(firstPage[0].Id, secondPage[0].Id); + Assert.NotEqual(firstPage[1].Id, secondPage[1].Id); + Assert.NotEqual(firstPage[2].Id, secondPage[2].Id); + Assert.NotEqual(firstPage[3].Id, secondPage[3].Id); + Assert.NotEqual(firstPage[4].Id, secondPage[4].Id); + } + } + } +} diff --git a/Octokit.Tests.Integration/Reactive/ObservableUserEmailsClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableUserEmailsClientTests.cs index f3cd8b81..9e746299 100644 --- a/Octokit.Tests.Integration/Reactive/ObservableUserEmailsClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/ObservableUserEmailsClientTests.cs @@ -7,15 +7,40 @@ namespace Octokit.Tests.Integration { public class ObservableUserEmailsClientTests { - [IntegrationTest] - public async Task CanGetEmail() + readonly ObservableUserEmailsClient _emailClient; + + public ObservableUserEmailsClientTests() { var github = Helper.GetAuthenticatedClient(); - var client = new ObservableUserEmailsClient(github); + _emailClient = new ObservableUserEmailsClient(github); + } - var email = await client.GetAll(); + [IntegrationTest] + public async Task CanGetEmail() + { + var email = await _emailClient.GetAll(); Assert.NotNull(email); } + + [IntegrationTest] + public async Task CanGetEmailWithApiOptions() + { + var email = await _emailClient.GetAll(ApiOptions.None); + Assert.NotNull(email); + } + + [IntegrationTest] + public async Task ReturnsCorrectCountOfEmailsWithoutStart() + { + var options = new ApiOptions + { + PageSize = 5, + PageCount = 1 + }; + + var emails = await _emailClient.GetAll(options).ToList(); + Assert.NotEmpty(emails); + } } } diff --git a/Octokit.Tests.Integration/Reactive/ObservableUserKeysClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableUserKeysClientTests.cs index cf56317b..4e48c55f 100644 --- a/Octokit.Tests.Integration/Reactive/ObservableUserKeysClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/ObservableUserKeysClientTests.cs @@ -68,7 +68,7 @@ namespace Octokit.Tests.Integration.Clients // Create a key string keyTitle = "title"; string keyData = "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAjo4DqFKg8dOxiz/yjypmN1A4itU5QOStyYrfOFuTinesU/2zm9hqxJ5BctIhgtSHJ5foxkhsiBji0qrUg73Q25BThgNg8YFE8njr4EwjmqSqW13akx/zLV0GFFU0SdJ2F6rBldhi93lMnl0ex9swBqa3eLTY8C+HQGBI6MQUMw+BKp0oFkz87Kv+Pfp6lt/Uo32ejSxML1PT5hTH5n+fyl0ied+sRmPGZWmWoHB5Bc9mox7lB6I6A/ZgjtBqbEEn4HQ2/6vp4ojKfSgA4Mm7XMu0bZzX0itKjH1QWD9Lr5apV1cmZsj49Xf8SHucTtH+bq98hb8OOXEGFzplwsX2MQ=="; - + var observable = _github.User.Keys.Create(new NewPublicKey(keyTitle, keyData)); var key = await observable; diff --git a/Octokit.Tests/Clients/AssigneesClientTests.cs b/Octokit.Tests/Clients/AssigneesClientTests.cs index 12615271..e5c56018 100644 --- a/Octokit.Tests/Clients/AssigneesClientTests.cs +++ b/Octokit.Tests/Clients/AssigneesClientTests.cs @@ -4,14 +4,13 @@ using System.Net; using System.Threading.Tasks; using NSubstitute; using Octokit.Internal; -using Octokit.Tests.Helpers; using Xunit; namespace Octokit.Tests.Clients { public class AssigneesClientTests { - public class TheGetForRepositoryMethod + public class TheGetAllMethod { [Fact] public void RequestsCorrectUrl() @@ -21,7 +20,33 @@ namespace Octokit.Tests.Clients client.GetAllForRepository("fake", "repo"); - connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/assignees")); + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repos/fake/repo/assignees"), + null, + AcceptHeaders.StableVersion, + Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new AssigneesClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + client.GetAllForRepository("fake", "repo", options); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repos/fake/repo/assignees"), + null, + AcceptHeaders.StableVersion, + options); } [Fact] @@ -33,6 +58,7 @@ namespace Octokit.Tests.Clients await Assert.ThrowsAsync(() => client.GetAllForRepository(null, "")); await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", null)); await Assert.ThrowsAsync(() => client.GetAllForRepository("", null)); + await Assert.ThrowsAsync(() => client.GetAllForRepository("owner", "name", null)); } } diff --git a/Octokit.Tests/Clients/AuthorizationsClientTests.cs b/Octokit.Tests/Clients/AuthorizationsClientTests.cs index af18967e..0724d76b 100644 --- a/Octokit.Tests/Clients/AuthorizationsClientTests.cs +++ b/Octokit.Tests/Clients/AuthorizationsClientTests.cs @@ -26,7 +26,7 @@ namespace Octokit.Tests.Clients public class TheGetAllMethod { [Fact] - public void GetsAListOfAuthorizations() + public void RequestsCorrectUrl() { var client = Substitute.For(); var authEndpoint = new AuthorizationsClient(client); @@ -35,7 +35,36 @@ namespace Octokit.Tests.Clients client.Received().GetAll( Arg.Is(u => u.ToString() == "authorizations"), - null); + Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var client = Substitute.For(); + var authEndpoint = new AuthorizationsClient(client); + + var options = new ApiOptions + { + StartPage = 1, + PageSize = 1, + PageCount = 1 + }; + + authEndpoint.GetAll(options); + + client.Received().GetAll( + Arg.Is(u => u.ToString() == "authorizations"), + options); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var client = Substitute.For(); + var authEndpoint = new AuthorizationsClient(client); + + await Assert.ThrowsAsync(() => authEndpoint.GetAll(null)); } } diff --git a/Octokit.Tests/Clients/Enterprise/EnterpriseOrganizationClientTests.cs b/Octokit.Tests/Clients/Enterprise/EnterpriseOrganizationClientTests.cs index 4b078b12..b3c815b5 100644 --- a/Octokit.Tests/Clients/Enterprise/EnterpriseOrganizationClientTests.cs +++ b/Octokit.Tests/Clients/Enterprise/EnterpriseOrganizationClientTests.cs @@ -17,7 +17,7 @@ namespace Octokit.Tests.Clients string expectedUri = "admin/organizations"; client.Create(new NewOrganization("org", "admin", "org name")); - + connection.Received().Post(Arg.Is(u => u.ToString() == expectedUri), Arg.Any()); } diff --git a/Octokit.Tests/Clients/GistsClientTests.cs b/Octokit.Tests/Clients/GistsClientTests.cs index a78efd70..56c31186 100644 --- a/Octokit.Tests/Clients/GistsClientTests.cs +++ b/Octokit.Tests/Clients/GistsClientTests.cs @@ -1,7 +1,6 @@ using NSubstitute; using Octokit; using Octokit.Internal; -using Octokit.Tests.Helpers; using System; using System.Collections.Generic; using System.Net; @@ -24,7 +23,7 @@ public class GistsClientTests } } - public class TheGetAllMethods + public class TheGetAllMethod { [Fact] public void RequestsCorrectGetAllUrl() @@ -42,6 +41,7 @@ public class GistsClientTests { var connection = Substitute.For(); var client = new GistsClient(connection); + DateTimeOffset since = DateTimeOffset.Now; client.GetAll(since); diff --git a/Octokit.Tests/Clients/ReleasesClientTests.cs b/Octokit.Tests/Clients/ReleasesClientTests.cs index b78ec427..10ce49a8 100644 --- a/Octokit.Tests/Clients/ReleasesClientTests.cs +++ b/Octokit.Tests/Clients/ReleasesClientTests.cs @@ -2,14 +2,13 @@ using System.IO; using System.Threading.Tasks; using NSubstitute; -using Octokit.Tests.Helpers; using Xunit; namespace Octokit.Tests.Clients { public class ReleasesClientTests { - public class TheGetReleasesMethod + public class TheGetAllMethod { [Fact] public void RequestsCorrectUrl() @@ -21,7 +20,29 @@ namespace Octokit.Tests.Clients client.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/releases"), null, - "application/vnd.github.v3"); + "application/vnd.github.v3", + Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var client = Substitute.For(); + var releasesClient = new ReleasesClient(client); + + var options = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + releasesClient.GetAll("fake", "repo", options); + + client.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/releases"), + null, + "application/vnd.github.v3", + options); } [Fact] @@ -31,6 +52,7 @@ namespace Octokit.Tests.Clients await Assert.ThrowsAsync(() => releasesClient.GetAll(null, "name")); await Assert.ThrowsAsync(() => releasesClient.GetAll("owner", null)); + await Assert.ThrowsAsync(() => releasesClient.GetAll("owner", "name", null)); } } diff --git a/Octokit.Tests/Clients/RepositoriesClientTests.cs b/Octokit.Tests/Clients/RepositoriesClientTests.cs index e74cc97b..f0f77b7d 100644 --- a/Octokit.Tests/Clients/RepositoriesClientTests.cs +++ b/Octokit.Tests/Clients/RepositoriesClientTests.cs @@ -404,7 +404,6 @@ namespace Octokit.Tests.Clients var request = new RepositoryRequest { - Affiliation = RepositoryAffiliation.Owner, Sort = RepositorySort.FullName }; @@ -790,5 +789,40 @@ namespace Octokit.Tests.Clients await Assert.ThrowsAsync(() => client.EditBranch("owner", "repo", "", update)); } } + + public class TheGetSha1Method + { + [Fact] + public void EnsuresNonNullArguments() + { + var client = new RepositoryCommitsClient(Substitute.For()); + + Assert.ThrowsAsync(() => client.GetSha1("", "name", "reference")); + Assert.ThrowsAsync(() => client.GetSha1("owner", "", "reference")); + Assert.ThrowsAsync(() => client.GetSha1("owner", "name", "")); + } + + [Fact] + public async Task EnsuresNonEmptyArguments() + { + var client = new RepositoryCommitsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetSha1(null, "name", "reference")); + await Assert.ThrowsAsync(() => client.GetSha1("owner", null, "reference")); + await Assert.ThrowsAsync(() => client.GetSha1("owner", "name", null)); + } + + [Fact] + public void GetsCorrectUrl() + { + var connection = Substitute.For(); + var client = new RepositoryCommitsClient(connection); + + client.GetSha1("owner", "name", "reference"); + + connection.Received() + .Get(Arg.Is(u => u.ToString() == "repos/owner/name/commits/reference"), null, AcceptHeaders.CommitReferenceSha1Preview); + } + } } } diff --git a/Octokit.Tests/Clients/RepositoryContentsClientTests.cs b/Octokit.Tests/Clients/RepositoryContentsClientTests.cs index 0e59b533..5dea73d8 100644 --- a/Octokit.Tests/Clients/RepositoryContentsClientTests.cs +++ b/Octokit.Tests/Clients/RepositoryContentsClientTests.cs @@ -100,7 +100,7 @@ namespace Octokit.Tests.Clients string expectedUri = "repos/org/repo/contents/path/to/file"; client.CreateFile("org", "repo", "path/to/file", new CreateFileRequest("message", "myfilecontents", "mybranch")); - + connection.Received().Put(Arg.Is(u => u.ToString() == expectedUri), Arg.Any()); } diff --git a/Octokit.Tests/Clients/UserAdministrationClientTests.cs b/Octokit.Tests/Clients/UserAdministrationClientTests.cs index 4174b06b..4712b24b 100644 --- a/Octokit.Tests/Clients/UserAdministrationClientTests.cs +++ b/Octokit.Tests/Clients/UserAdministrationClientTests.cs @@ -29,7 +29,7 @@ namespace Octokit.Tests.Clients client.Create(new NewUser("name", "email@company.com")); connection.Received().Post( - Arg.Is(u => u.ToString() == expectedUri), + Arg.Is(u => u.ToString() == expectedUri), Arg.Any()); } @@ -38,13 +38,13 @@ namespace Octokit.Tests.Clients { var connection = Substitute.For(); var client = new UserAdministrationClient(connection); - + client.Create(new NewUser("name", "email@company.com")); connection.Received().Post( - Arg.Any(), + Arg.Any(), Arg.Is(a => - a.Login == "name" && + a.Login == "name" && a.Email == "email@company.com")); } } diff --git a/Octokit.Tests/Clients/UserEmailsClientTests.cs b/Octokit.Tests/Clients/UserEmailsClientTests.cs index b3564796..9ea83029 100644 --- a/Octokit.Tests/Clients/UserEmailsClientTests.cs +++ b/Octokit.Tests/Clients/UserEmailsClientTests.cs @@ -1,7 +1,7 @@ -using NSubstitute; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; +using NSubstitute; using Xunit; namespace Octokit.Tests.Clients @@ -11,7 +11,7 @@ namespace Octokit.Tests.Clients public class TheGetAllMethod { [Fact] - public void GetsCorrectUrl() + public void RequestsCorrectUrl() { var connection = Substitute.For(); var client = new UserEmailsClient(connection); @@ -19,7 +19,36 @@ namespace Octokit.Tests.Clients client.GetAll(); connection.Received(1) - .GetAll(Arg.Is(u => u.ToString() == "user/emails")); + .GetAll(Arg.Is(u => u.ToString() == "user/emails"), + Args.ApiOptions); + } + + [Fact] + public void RequestsCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new UserEmailsClient(connection); + + var options = new ApiOptions + { + PageCount = 1, + PageSize = 1, + StartPage = 1 + }; + + client.GetAll(options); + + connection.Received(1) + .GetAll(Arg.Is(u => u.ToString() == "user/emails"), + options); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var releasesClient = new UserEmailsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => releasesClient.GetAll(null)); } } diff --git a/Octokit.Tests/Helpers/Arg.cs b/Octokit.Tests/Helpers/Arg.cs index 391255fa..5408f72d 100644 --- a/Octokit.Tests/Helpers/Arg.cs +++ b/Octokit.Tests/Helpers/Arg.cs @@ -67,5 +67,10 @@ namespace Octokit.Tests { get { return Arg.Any(); } } + + public static ApiOptions ApiOptions + { + get { return Arg.Any(); } + } } } diff --git a/Octokit.Tests/Http/ApiConnectionTests.cs b/Octokit.Tests/Http/ApiConnectionTests.cs index f0dd7b6d..5f3b7d22 100644 --- a/Octokit.Tests/Http/ApiConnectionTests.cs +++ b/Octokit.Tests/Http/ApiConnectionTests.cs @@ -90,13 +90,13 @@ namespace Octokit.Tests.Http new Response(), new List { new object(), new object() }); var connection = Substitute.For(); - connection.Get>(Args.Uri, null, null).Returns(Task.FromResult(response)); + connection.Get>(Args.Uri, Args.EmptyDictionary, null).Returns(Task.FromResult(response)); var apiConnection = new ApiConnection(connection); var data = await apiConnection.GetAll(getAllUri); Assert.Equal(2, data.Count); - connection.Received().Get>(getAllUri, null, null); + connection.Received().Get>(getAllUri, Args.EmptyDictionary, null); } [Fact] diff --git a/Octokit.Tests/Http/RedirectHandlerTests.cs b/Octokit.Tests/Http/RedirectHandlerTests.cs index 8f5b65d3..845067e2 100644 --- a/Octokit.Tests/Http/RedirectHandlerTests.cs +++ b/Octokit.Tests/Http/RedirectHandlerTests.cs @@ -189,7 +189,7 @@ namespace Octokit.Tests.Http _response2 = response2; } - protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (!_Response1Sent) { diff --git a/Octokit.Tests/Models/ReadOnlyPagedCollectionTests.cs b/Octokit.Tests/Models/ReadOnlyPagedCollectionTests.cs index 6fa1e4c2..908ff7e7 100644 --- a/Octokit.Tests/Models/ReadOnlyPagedCollectionTests.cs +++ b/Octokit.Tests/Models/ReadOnlyPagedCollectionTests.cs @@ -33,6 +33,70 @@ namespace Octokit.Tests.Models Assert.NotNull(nextPage); Assert.Equal(2, nextPage.Count); } + + [Fact] + public async Task WhenNoInformationSetReturnsNull() + { + var nextPageUrl = new Uri("https://example.com/page/2"); + var nextPageResponse = Task.Factory.StartNew>>(() => + new ApiResponse>(new Response(), new List { new object(), new object() })); + + var links = new Dictionary(); + var scopes = new List(); + var httpResponse = Substitute.For(); + httpResponse.ApiInfo.Returns(new ApiInfo(links, scopes, scopes, "etag", new RateLimit(new Dictionary()))); + + var response = new ApiResponse>(httpResponse, new List()); + var connection = Substitute.For(); + + connection.Get>(nextPageUrl, null, null).Returns(nextPageResponse); + + var pagedCollection = new ReadOnlyPagedCollection( + response, + nextPageUri => connection.Get>(nextPageUrl, null, null)); + + var nextPage = await pagedCollection.GetNextPage(); + + Assert.Null(nextPage); + } + + [Fact] + public async Task WhenInlineFuncKillsPaginationReturnNull() + { + var nextPageUrl = new Uri("https://example.com/page/2"); + var nextPageResponse = Task.Factory.StartNew>>(() => + new ApiResponse>(new Response(), new List { new object(), new object() })); + + var links = new Dictionary { { "next", nextPageUrl } }; + var scopes = new List(); + var httpResponse = Substitute.For(); + httpResponse.ApiInfo.Returns(new ApiInfo(links, scopes, scopes, "etag", new RateLimit(new Dictionary()))); + + var response = new ApiResponse>(httpResponse, new List()); + var connection = Substitute.For(); + + connection.Get>(nextPageUrl, null, null).Returns(nextPageResponse); + + var pageCount = 1; + + var pagedCollection = new ReadOnlyPagedCollection( + response, + nextPageUri => + { + if (pageCount > 1) + { + return null; + } + pageCount++; + return connection.Get>(nextPageUrl, null, null); + }); + + var first = await pagedCollection.GetNextPage(); + var second = await pagedCollection.GetNextPage(); + + Assert.NotNull(first); + Assert.Null(second); + } } } } diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index 5c6484c3..8fd268a0 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -198,6 +198,7 @@ + @@ -212,6 +213,7 @@ + diff --git a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs index a1fc9769..ec8de495 100644 --- a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs +++ b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseLdapClientTests.cs @@ -10,7 +10,7 @@ namespace Octokit.Tests public class TheUpdateUserMappingMethod { readonly string _distinguishedName = "uid=test-user,ou=users,dc=company,dc=com"; - + [Fact] public void CallsIntoClient() { @@ -20,11 +20,11 @@ namespace Octokit.Tests client.UpdateUserMapping("test-user", new NewLdapMapping(_distinguishedName)); github.Enterprise.Ldap.Received(1).UpdateUserMapping( Arg.Is(a => a == "test-user"), - Arg.Is(a => + Arg.Is(a => a.LdapDistinguishedName == _distinguishedName)); } } - + public class TheQueueSyncUserMappingMethod { [Fact] @@ -38,11 +38,11 @@ namespace Octokit.Tests Arg.Is(a => a == "test-user")); } } - + public class TheUpdateTeamMappingMethod { readonly string _distinguishedName = "cn=test-team,ou=groups,dc=company,dc=com"; - + [Fact] public void CallsIntoClient() { @@ -52,11 +52,11 @@ namespace Octokit.Tests client.UpdateTeamMapping(1, new NewLdapMapping(_distinguishedName)); github.Enterprise.Ldap.Received(1).UpdateTeamMapping( Arg.Is(a => a == 1), - Arg.Is(a => + Arg.Is(a => a.LdapDistinguishedName == _distinguishedName)); } } - + public class TheQueueSyncTeamMappingMethod { [Fact] diff --git a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseOrganizationClientTests.cs b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseOrganizationClientTests.cs index 47046e05..5e849d84 100644 --- a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseOrganizationClientTests.cs +++ b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseOrganizationClientTests.cs @@ -17,9 +17,9 @@ namespace Octokit.Tests client.Create(new NewOrganization("org", "admin", "org name")); github.Enterprise.Organization.Received(1).Create( - Arg.Is(a => - a.Login == "org" - && a.Admin == "admin" + Arg.Is(a => + a.Login == "org" + && a.Admin == "admin" && a.ProfileName == "org name")); } } diff --git a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseSearchIndexingClientTests.cs b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseSearchIndexingClientTests.cs index 491acbfc..e185081f 100644 --- a/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseSearchIndexingClientTests.cs +++ b/Octokit.Tests/Reactive/Enterprise/ObservableEnterpriseSearchIndexingClientTests.cs @@ -17,7 +17,7 @@ namespace Octokit.Tests client.Queue("org"); github.Enterprise.SearchIndexing.Received(1). - Queue(Arg.Is( "org" )); + Queue(Arg.Is("org")); client.Queue("org", "repo"); github.Enterprise.SearchIndexing.Received(1). diff --git a/Octokit.Tests/Reactive/ObservableAuthorizationsClientTests.cs b/Octokit.Tests/Reactive/ObservableAuthorizationsClientTests.cs new file mode 100644 index 00000000..7fe3bea0 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableAuthorizationsClientTests.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableAuthorizationsClientTests + { + public class TheGetAllMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var client = Substitute.For(); + var authEndpoint = new ObservableAuthorizationsClient(client); + + authEndpoint.GetAll(); + + client.Connection.Received(1).Get>(Arg.Is(u => u.ToString() == "authorizations"), + Arg.Is>(dictionary => dictionary.Count == 0), null); + } + + [Fact] + public void RequestsCorrectUrlWithApiOption() + { + var client = Substitute.For(); + var authEndpoint = new ObservableAuthorizationsClient(client); + + authEndpoint.GetAll(ApiOptions.None); + + client.Connection.Received(1).Get>(Arg.Is(u => u.ToString() == "authorizations"), + Arg.Is>(dictionary => dictionary.Count == 0), null); + } + + [Fact] + public async Task EnsuresArgumentsNotNull() + { + var client = Substitute.For(); + var authEndpoint = new ObservableAuthorizationsClient(client); + + await Assert.ThrowsAsync(() => authEndpoint.GetAll(null).ToTask()); + } + } + + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new ObservableAuthorizationsClient(null)); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs b/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs index aa23f958..b09a498a 100644 --- a/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableIssuesClientTests.cs @@ -329,11 +329,11 @@ public class ObservableIssuesClientTests var gitHubClient = Substitute.For(); var client = new ObservableIssuesClient(gitHubClient); - Assert.Throws(() => client.Create(null, "name", new NewIssue("title"))); - Assert.Throws(() => client.Create("", "name", new NewIssue("x"))); - Assert.Throws(() => client.Create("owner", null, new NewIssue("x"))); - Assert.Throws(() => client.Create("owner", "", new NewIssue("x"))); - Assert.Throws(() => client.Create("owner", "name", null)); + Assert.Throws(() => client.Update(null, "name", 42, new IssueUpdate())); + Assert.Throws(() => client.Update("", "name", 42, new IssueUpdate())); + Assert.Throws(() => client.Update("owner", null, 42, new IssueUpdate())); + Assert.Throws(() => client.Update("owner", "", 42, new IssueUpdate())); + Assert.Throws(() => client.Update("owner", "name", 42, null)); } } diff --git a/Octokit.Tests/Reactive/ObservablePullRequestsClientTests.cs b/Octokit.Tests/Reactive/ObservablePullRequestsClientTests.cs index 929ed17f..f9d7eec1 100644 --- a/Octokit.Tests/Reactive/ObservablePullRequestsClientTests.cs +++ b/Octokit.Tests/Reactive/ObservablePullRequestsClientTests.cs @@ -281,7 +281,7 @@ namespace Octokit.Tests.Reactive public async Task FetchesAllCommitsForPullRequest() { var commit = new PullRequestCommit(null, null, null, null, null, Enumerable.Empty(), null, null); - var expectedUrl = string.Format("repos/fake/repo/pulls/42/commits"); + var expectedUrl = "repos/fake/repo/pulls/42/commits"; var gitHubClient = Substitute.For(); var connection = Substitute.For(); IApiResponse> response = new ApiResponse> @@ -320,7 +320,7 @@ namespace Octokit.Tests.Reactive public async Task FetchesAllFilesForPullRequest() { var file = new PullRequestFile(null, null, null, 0, 0, 0, null, null, null, null); - var expectedUrl = string.Format("repos/fake/repo/pulls/42/files"); + var expectedUrl = "repos/fake/repo/pulls/42/files"; var gitHubClient = Substitute.For(); var connection = Substitute.For(); IApiResponse> response = new ApiResponse> diff --git a/Octokit.Tests/Reactive/ObservableRepositoryCommitsClientTests.cs b/Octokit.Tests/Reactive/ObservableRepositoryCommitsClientTests.cs new file mode 100644 index 00000000..bcd91f37 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableRepositoryCommitsClientTests.cs @@ -0,0 +1,59 @@ +using System; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableRepositoryCommitsClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresArgument() + { + Assert.Throws(() => new ObservableRepositoryCommitsClient(null)); + } + } + + public class TheGetSha1Method + { + [Fact] + public void EnsuresNonEmptyArguments() + { + var client = new ObservableRepositoryCommitsClient(Substitute.For()); + + Assert.ThrowsAsync(() => client.GetSha1("", "name", "reference").ToTask()); + Assert.ThrowsAsync(() => client.GetSha1("owner", "", "reference").ToTask()); + Assert.ThrowsAsync(() => client.GetSha1("owner", "name", "").ToTask()); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableRepositoryCommitsClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetSha1(null, "name", "reference").ToTask()); + await Assert.ThrowsAsync(() => client.GetSha1("owner", null, "reference").ToTask()); + await Assert.ThrowsAsync(() => client.GetSha1("owner", "name", null).ToTask()); + } + + [Fact] + public void GetsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositoryCommitsClient(gitHubClient); + + client.GetSha1("owner", "name", "reference"); + + gitHubClient + .Received() + .Repository + .Commit + .GetSha1("owner", "name", "reference"); + } + } + } +} diff --git a/Octokit.Tests/Reactive/ObservableUserAdministrationClientTests.cs b/Octokit.Tests/Reactive/ObservableUserAdministrationClientTests.cs index d247ddb3..682a09b6 100644 --- a/Octokit.Tests/Reactive/ObservableUserAdministrationClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableUserAdministrationClientTests.cs @@ -19,7 +19,7 @@ namespace Octokit.Tests.Reactive client.Create(new NewUser("auser", "email@company.com")); gitHubClient.User.Administration.Received().Create( - Arg.Is(a => + Arg.Is(a => a.Login == "auser" && a.Email == "email@company.com")); } diff --git a/Octokit.Tests/Reactive/ObservableUserEmailsClientTests.cs b/Octokit.Tests/Reactive/ObservableUserEmailsClientTests.cs index 1e62a86b..ea7dc688 100644 --- a/Octokit.Tests/Reactive/ObservableUserEmailsClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableUserEmailsClientTests.cs @@ -1,7 +1,7 @@ -using NSubstitute; -using Octokit.Reactive; -using System; +using System; using System.Collections.Generic; +using NSubstitute; +using Octokit.Reactive; using Xunit; namespace Octokit.Tests @@ -18,25 +18,35 @@ namespace Octokit.Tests public class TheGetAllMethod { + private static readonly Uri _expectedUri = new Uri("user/emails", UriKind.Relative); + [Fact] public void GetsCorrectUrl() { - var expectedUri = new Uri("user/emails", UriKind.Relative); var github = Substitute.For(); var client = new ObservableUserEmailsClient(github); client.GetAll(); - github.Connection.Received(1).GetResponse>(expectedUri); + github.Connection.Received(1).Get>(_expectedUri, + Arg.Is>(dictionary => dictionary.Count == 0), null); + } + + [Fact] + public void GetsCorrectUrlWithApiOption() + { + var github = Substitute.For(); + var client = new ObservableUserEmailsClient(github); + + client.GetAll(ApiOptions.None); + + github.Connection.Received(1).Get>(_expectedUri, + Arg.Is>(dictionary => dictionary.Count == 0), null); } } public class TheAddMethod { - public IGitHubClient GitHubClient; - - public ObservableUserEmailsClient Client; - [Fact] public void CallsAddOnClient() { diff --git a/Octokit/Clients/AssigneesClient.cs b/Octokit/Clients/AssigneesClient.cs index f66b5dc1..58ca8666 100644 --- a/Octokit/Clients/AssigneesClient.cs +++ b/Octokit/Clients/AssigneesClient.cs @@ -30,7 +30,25 @@ namespace Octokit Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - return ApiConnection.GetAll(ApiUrls.Assignees(owner, name)); + return GetAllForRepository(owner, name, ApiOptions.None); + } + + /// + /// Gets all the available assignees (owner + collaborators) to which issues may be assigned. + /// + /// The owner of the repository + /// The name of the repository + /// The options to change API's response. + /// + public Task> GetAllForRepository(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(options, "options"); + + var endpoint = ApiUrls.Assignees(owner, name); + + return ApiConnection.GetAll(endpoint, null, AcceptHeaders.StableVersion, options); } /// diff --git a/Octokit/Clients/AuthorizationsClient.cs b/Octokit/Clients/AuthorizationsClient.cs index 440918a5..92a4cf4b 100644 --- a/Octokit/Clients/AuthorizationsClient.cs +++ b/Octokit/Clients/AuthorizationsClient.cs @@ -33,10 +33,30 @@ namespace Octokit /// Thrown when the current user does not have permission to make the request. /// /// Thrown when a general API error occurs. - /// A list of s. + /// A list of s for the authenticated user. public Task> GetAll() { - return ApiConnection.GetAll(ApiUrls.Authorizations(), null); + return GetAll(ApiOptions.None); + } + + /// + /// Gets all s for the authenticated user. + /// + /// + /// This method requires authentication. + /// See the API documentation for more information. + /// + /// Options for changing the API response + /// + /// Thrown when the current user does not have permission to make the request. + /// + /// Thrown when a general API error occurs. + /// A list of s for the authenticated user. + public Task> GetAll(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Authorizations(), options); } /// diff --git a/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs index 6357da50..1980dc6f 100644 --- a/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs +++ b/Octokit/Clients/Enterprise/EnterpriseLdapClient.cs @@ -45,7 +45,7 @@ namespace Octokit public async Task QueueSyncUserMapping(string userName) { Ensure.ArgumentNotNull(userName, "userName"); - + var endpoint = ApiUrls.EnterpriseLdapUserSync(userName); var response = await Connection.Post(endpoint); @@ -56,7 +56,7 @@ namespace Octokit return response.Body; } - + /// /// Update the LDAP mapping for a team on a GitHub Enterprise appliance (must be Site Admin user). /// @@ -87,7 +87,7 @@ namespace Octokit public async Task QueueSyncTeamMapping(int teamId) { Ensure.ArgumentNotNull(teamId, "teamId"); - + var endpoint = ApiUrls.EnterpriseLdapTeamSync(teamId); var response = await Connection.Post(endpoint); diff --git a/Octokit/Clients/Enterprise/EnterpriseSearchIndexingClient.cs b/Octokit/Clients/Enterprise/EnterpriseSearchIndexingClient.cs index 7e157bfb..2351a2f1 100644 --- a/Octokit/Clients/Enterprise/EnterpriseSearchIndexingClient.cs +++ b/Octokit/Clients/Enterprise/EnterpriseSearchIndexingClient.cs @@ -146,7 +146,7 @@ namespace Octokit public async Task QueueAllCode(string owner) { Ensure.ArgumentNotNull(owner, "owner"); - + var endpoint = ApiUrls.EnterpriseSearchIndexing(); var target = new SearchIndexTarget(string.Format(CultureInfo.InvariantCulture, "{0}/*/code", owner)); diff --git a/Octokit/Clients/IAssigneesClient.cs b/Octokit/Clients/IAssigneesClient.cs index 2a1e0cdf..f3bc5b79 100644 --- a/Octokit/Clients/IAssigneesClient.cs +++ b/Octokit/Clients/IAssigneesClient.cs @@ -19,6 +19,15 @@ namespace Octokit /// Task> GetAllForRepository(string owner, string name); + /// + /// Gets all the available assignees (owner + collaborators) to which issues may be assigned. + /// + /// The owner of the repository + /// The name of the repository + /// The options to chagne API's response. + /// + Task> GetAllForRepository(string owner, string name, ApiOptions options); + /// /// Checks to see if a user is an assignee for a repository. /// diff --git a/Octokit/Clients/IAuthorizationsClient.cs b/Octokit/Clients/IAuthorizationsClient.cs index 81b8db48..c78a048d 100644 --- a/Octokit/Clients/IAuthorizationsClient.cs +++ b/Octokit/Clients/IAuthorizationsClient.cs @@ -26,11 +26,28 @@ namespace Octokit /// Thrown when the current user does not have permission to make the request. /// /// Thrown when a general API error occurs. - /// A list of s. + /// A list of s for the authenticated user. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "It's an API call, so it's not a property.")] Task> GetAll(); + /// + /// Gets all s for the authenticated user. + /// + /// + /// This method requires authentication. + /// See the API documentation for more information. + /// + /// Options for changing the API response + /// + /// Thrown when the current user does not have permission to make the request. + /// + /// Thrown when a general API error occurs. + /// A list of s for the authenticated user. + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification = "It's an API call, so it's not a property.")] + Task> GetAll(ApiOptions options); + /// /// Gets a specific for the authenticated user. /// diff --git a/Octokit/Clients/IReleasesClient.cs b/Octokit/Clients/IReleasesClient.cs index d04b0cd3..435f67ae 100644 --- a/Octokit/Clients/IReleasesClient.cs +++ b/Octokit/Clients/IReleasesClient.cs @@ -26,6 +26,19 @@ namespace Octokit /// The list of s for the specified repository. Task> GetAll(string owner, string name); + /// + /// Gets all s for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The repository's owner + /// The repository's name + /// Options for changing the API response + /// Thrown when a general API error occurs. + /// The list of s for the specified repository. + Task> GetAll(string owner, string name, ApiOptions options); + /// /// Gets a single for the specified repository. /// diff --git a/Octokit/Clients/IRepositoriesClient.cs b/Octokit/Clients/IRepositoriesClient.cs index d88480a6..b5db4ad6 100644 --- a/Octokit/Clients/IRepositoriesClient.cs +++ b/Octokit/Clients/IRepositoriesClient.cs @@ -274,7 +274,7 @@ namespace Octokit /// Access GitHub's Releases API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/repos/releases/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/releases/ /// IReleasesClient Release { get; } diff --git a/Octokit/Clients/IRepositoryCommitsClient.cs b/Octokit/Clients/IRepositoryCommitsClient.cs index f2b09f2b..bd486192 100644 --- a/Octokit/Clients/IRepositoryCommitsClient.cs +++ b/Octokit/Clients/IRepositoryCommitsClient.cs @@ -50,5 +50,14 @@ namespace Octokit /// Used to filter list of commits returned /// Task> GetAll(string owner, string name, CommitRequest request); + + /// + /// Get the SHA-1 of a commit reference + /// + /// The owner of the repository + /// The name of the repository + /// The repository reference + /// + Task GetSha1(string owner, string name, string reference); } } diff --git a/Octokit/Clients/IUserEmailsClient.cs b/Octokit/Clients/IUserEmailsClient.cs index b653d218..4fb9fb12 100644 --- a/Octokit/Clients/IUserEmailsClient.cs +++ b/Octokit/Clients/IUserEmailsClient.cs @@ -22,6 +22,17 @@ namespace Octokit [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] Task> GetAll(); + /// + /// Gets all email addresses for the authenticated user. + /// + /// + /// http://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user + /// + /// Options for changing the API response + /// The es for the authenticated user. + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + Task> GetAll(ApiOptions options); + /// /// Adds email addresses for the authenticated user. /// diff --git a/Octokit/Clients/ReleasesClient.cs b/Octokit/Clients/ReleasesClient.cs index fae7bc4d..7fd95fea 100644 --- a/Octokit/Clients/ReleasesClient.cs +++ b/Octokit/Clients/ReleasesClient.cs @@ -36,8 +36,28 @@ namespace Octokit Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "repository"); + return GetAll(owner, name, ApiOptions.None); + } + + /// + /// Gets all s for the specified repository. + /// + /// + /// See the API documentation for more information. + /// + /// The repository's owner + /// The repository's name + /// Options for changing the API response + /// Thrown when a general API error occurs. + /// The list of s for the specified repository. + public Task> GetAll(string owner, string name, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "repository"); + Ensure.ArgumentNotNull(options, "options"); + var endpoint = ApiUrls.Releases(owner, name); - return ApiConnection.GetAll(endpoint, null, AcceptHeaders.StableVersion); + return ApiConnection.GetAll(endpoint, null, AcceptHeaders.StableVersion, options); } /// diff --git a/Octokit/Clients/RepositoriesClient.cs b/Octokit/Clients/RepositoriesClient.cs index 0b034fe4..590637d0 100644 --- a/Octokit/Clients/RepositoriesClient.cs +++ b/Octokit/Clients/RepositoriesClient.cs @@ -396,7 +396,7 @@ namespace Octokit /// Access GitHub's Releases API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/repos/releases/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/releases/ /// public IReleasesClient Release { get; private set; } diff --git a/Octokit/Clients/RepositoryCommitsClient.cs b/Octokit/Clients/RepositoryCommitsClient.cs index 0e8ad1b2..e5fa9613 100644 --- a/Octokit/Clients/RepositoryCommitsClient.cs +++ b/Octokit/Clients/RepositoryCommitsClient.cs @@ -79,5 +79,21 @@ namespace Octokit return _apiConnection.GetAll(ApiUrls.RepositoryCommits(owner, name), request.ToParametersDictionary()); } + + /// + /// Get the SHA-1 of a commit reference + /// + /// The owner of the repository + /// The name of the repository + /// The repository reference + /// + public Task GetSha1(string owner, string name, string reference) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(reference, "reference"); + + return _apiConnection.Get(ApiUrls.RepositoryCommit(owner, name, reference), null, AcceptHeaders.CommitReferenceSha1Preview); + } } } \ No newline at end of file diff --git a/Octokit/Clients/UserAdministrationClient.cs b/Octokit/Clients/UserAdministrationClient.cs index a39fcb8d..1b7a15e6 100644 --- a/Octokit/Clients/UserAdministrationClient.cs +++ b/Octokit/Clients/UserAdministrationClient.cs @@ -218,7 +218,7 @@ namespace Octokit { Ensure.ArgumentNotNull(keyId, "keyId"); var endpoint = ApiUrls.UserAdministrationPublicKeys(keyId); - + var response = ((HttpStatusCode)await Connection.Delete(endpoint)); if (response != HttpStatusCode.NoContent) { diff --git a/Octokit/Clients/UserEmailsClient.cs b/Octokit/Clients/UserEmailsClient.cs index d503e853..fc638cd0 100644 --- a/Octokit/Clients/UserEmailsClient.cs +++ b/Octokit/Clients/UserEmailsClient.cs @@ -30,7 +30,21 @@ namespace Octokit /// The es for the authenticated user. public Task> GetAll() { - return ApiConnection.GetAll(ApiUrls.Emails()); + return GetAll(ApiOptions.None); + } + + /// + /// Gets all email addresses for the authenticated user. + /// + /// + /// http://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user + /// + /// The es for the authenticated user. + public Task> GetAll(ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.Emails(), options); } /// diff --git a/Octokit/GitHubClient.cs b/Octokit/GitHubClient.cs index 105a7da1..69577776 100644 --- a/Octokit/GitHubClient.cs +++ b/Octokit/GitHubClient.cs @@ -147,7 +147,7 @@ namespace Octokit /// Access GitHub's Authorization API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/oauth_authorizations/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/oauth_authorizations/ /// public IAuthorizationsClient Authorization { get; private set; } @@ -155,7 +155,7 @@ namespace Octokit /// Access GitHub's Activity API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/activity/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/activity/ /// public IActivitiesClient Activity { get; private set; } @@ -163,7 +163,7 @@ namespace Octokit /// Access GitHub's Issue API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/issues/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/issues/ /// public IIssuesClient Issue { get; private set; } @@ -171,7 +171,7 @@ namespace Octokit /// Access GitHub's Miscellaneous API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/misc/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/misc/ /// public IMiscellaneousClient Miscellaneous { get; private set; } @@ -179,7 +179,7 @@ namespace Octokit /// Access GitHub's OAuth API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/oauth/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/oauth/ /// public IOauthClient Oauth { get; private set; } @@ -187,7 +187,7 @@ namespace Octokit /// Access GitHub's Organizations API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/orgs/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/orgs/ /// public IOrganizationsClient Organization { get; private set; } @@ -195,7 +195,7 @@ namespace Octokit /// Access GitHub's Pull Requests API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/pulls/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/pulls/ /// public IPullRequestsClient PullRequest { get; private set; } @@ -203,7 +203,7 @@ namespace Octokit /// Access GitHub's Repositories API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/repos/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/ /// public IRepositoriesClient Repository { get; private set; } @@ -211,7 +211,7 @@ namespace Octokit /// Access GitHub's Gists API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/gists/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/gists/ /// public IGistsClient Gist { get; private set; } @@ -219,7 +219,7 @@ namespace Octokit /// Access GitHub's Releases API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/repos/releases/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/releases/ /// [Obsolete("Use Repository.Release instead")] public IReleasesClient Release @@ -233,7 +233,7 @@ namespace Octokit /// Access GitHub's Public Keys API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/users/keys/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/users/keys/ /// public ISshKeysClient SshKey { get; private set; } @@ -241,7 +241,7 @@ namespace Octokit /// Access GitHub's Users API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/users/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/users/ /// public IUsersClient User { get; private set; } @@ -250,7 +250,7 @@ namespace Octokit /// Access GitHub's Notifications API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/activity/notifications/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/activity/notifications/ /// public INotificationsClient Notification { get; private set; } @@ -258,7 +258,7 @@ namespace Octokit /// Access GitHub's Git Data API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/git/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/git/ /// [Obsolete("Use Git instead")] public IGitDatabaseClient GitDatabase { get { return Git; } } @@ -267,7 +267,7 @@ namespace Octokit /// Access GitHub's Git Data API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/git/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/git/ /// public IGitDatabaseClient Git { get; private set; } @@ -275,7 +275,7 @@ namespace Octokit /// Access GitHub's Search API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/search/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/search/ /// public ISearchClient Search { get; private set; } @@ -284,7 +284,7 @@ namespace Octokit /// Access GitHub's Deployments API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/repos/deployments/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/deployments/ /// public IDeploymentsClient Deployment { get; private set; } @@ -292,7 +292,7 @@ namespace Octokit /// Access GitHub's Enterprise API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/enterprise/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/enterprise/ /// public IEnterpriseClient Enterprise { get; private set; } diff --git a/Octokit/Helpers/AcceptHeaders.cs b/Octokit/Helpers/AcceptHeaders.cs index d94e7e15..21c0047e 100644 --- a/Octokit/Helpers/AcceptHeaders.cs +++ b/Octokit/Helpers/AcceptHeaders.cs @@ -13,5 +13,7 @@ public const string ProtectedBranchesApiPreview = "application/vnd.github.loki-preview+json"; public const string StarCreationTimestamps = "application/vnd.github.v3.star+json"; + + public const string CommitReferenceSha1Preview = "application/vnd.github.chitauri-preview+sha"; } } diff --git a/Octokit/Helpers/ApiExtensions.cs b/Octokit/Helpers/ApiExtensions.cs index 5a3c9a15..81b1ab8e 100644 --- a/Octokit/Helpers/ApiExtensions.cs +++ b/Octokit/Helpers/ApiExtensions.cs @@ -26,7 +26,7 @@ namespace Octokit Ensure.ArgumentNotNull(connection, "connection"); Ensure.ArgumentNotNull(uri, "uri"); - return connection.GetAll(uri, null); + return connection.GetAll(uri, ApiOptions.None); } /// diff --git a/Octokit/Helpers/Pagination.cs b/Octokit/Helpers/Pagination.cs new file mode 100644 index 00000000..1acf39ea --- /dev/null +++ b/Octokit/Helpers/Pagination.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace Octokit +{ + internal static class Pagination + { + internal static IDictionary Setup(IDictionary parameters, ApiOptions options) + { + parameters = parameters ?? new Dictionary(); + + if (options.PageSize.HasValue) + { + parameters.Add("per_page", options.PageSize.Value.ToString(CultureInfo.InvariantCulture)); + } + + if (options.StartPage.HasValue) + { + parameters.Add("page", options.StartPage.Value.ToString(CultureInfo.InvariantCulture)); + } + + return parameters; + } + + internal static bool ShouldContinue( + Uri uri, + ApiOptions options) + { + if (uri == null) + { + return false; + } + + if (uri.Query.Contains("page=") && options.PageCount.HasValue) + { + var allValues = ToQueryStringDictionary(uri); + + string pageValue; + if (allValues.TryGetValue("page", out pageValue)) + { + var startPage = options.StartPage ?? 1; + var pageCount = options.PageCount.Value; + + var endPage = startPage + pageCount; + if (pageValue.Equals(endPage.ToString(CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase)) + { + return false; + } + } + } + + return true; + } + + static Dictionary ToQueryStringDictionary(Uri uri) + { + return uri.Query.Split('&') + .Select(keyValue => + { + var indexOf = keyValue.IndexOf('='); + if (indexOf > 0) + { + var key = keyValue.Substring(0, indexOf); + var value = keyValue.Substring(indexOf + 1); + return new KeyValuePair(key, value); + } + + //just a plain old value, return it + return new KeyValuePair(keyValue, null); + }) + .ToDictionary(x => x.Key, x => x.Value); + } + } +} diff --git a/Octokit/Http/ApiConnection.cs b/Octokit/Http/ApiConnection.cs index 33a77f6e..c1d2710a 100644 --- a/Octokit/Http/ApiConnection.cs +++ b/Octokit/Http/ApiConnection.cs @@ -115,7 +115,20 @@ namespace Octokit /// Thrown when an API error occurs. public Task> GetAll(Uri uri) { - return GetAll(uri, null, null); + return GetAll(uri, ApiOptions.None); + } + + /// + /// Gets all API resources in the list at the specified URI. + /// + /// Type of the API resource in the list. + /// URI of the API resource to get + /// Options for changing the API response + /// of the The API resources in the list. + /// Thrown when an API error occurs. + public Task> GetAll(Uri uri, ApiOptions options) + { + return GetAll(uri, null, null, options); } /// @@ -128,7 +141,21 @@ namespace Octokit /// Thrown when an API error occurs. public Task> GetAll(Uri uri, IDictionary parameters) { - return GetAll(uri, parameters, null); + return GetAll(uri, parameters, null, ApiOptions.None); + } + + /// + /// Gets all API resources in the list at the specified URI. + /// + /// Type of the API resource in the list. + /// URI of the API resource to get + /// Parameters to add to the API request + /// Options for changing the API response + /// of the The API resources in the list. + /// Thrown when an API error occurs. + public Task> GetAll(Uri uri, IDictionary parameters, ApiOptions options) + { + return GetAll(uri, parameters, null, options); } /// @@ -148,6 +175,17 @@ namespace Octokit .ConfigureAwait(false), uri); } + public Task> GetAll(Uri uri, IDictionary parameters, string accepts, ApiOptions options) + { + Ensure.ArgumentNotNull(uri, "uri"); + Ensure.ArgumentNotNull(options, "options"); + + parameters = Pagination.Setup(parameters, options); + + return _pagination.GetAllPages(async () => await GetPage(uri, parameters, accepts, options) + .ConfigureAwait(false), uri); + } + /// /// Creates a new API resource in the list at the specified URI. /// @@ -498,5 +536,30 @@ namespace Octokit response, nextPageUri => Connection.Get>(nextPageUri, parameters, accepts)); } + + async Task> GetPage( + Uri uri, + IDictionary parameters, + string accepts, + ApiOptions options) + { + Ensure.ArgumentNotNull(uri, "uri"); + + var connection = Connection; + + var response = await connection.Get>(uri, parameters, accepts).ConfigureAwait(false); + return new ReadOnlyPagedCollection( + response, + nextPageUri => + { + var shouldContinue = Pagination.ShouldContinue( + nextPageUri, + options); + + return shouldContinue + ? connection.Get>(nextPageUri, parameters, accepts) + : null; + }); + } } } diff --git a/Octokit/Http/ApiInfo.cs b/Octokit/Http/ApiInfo.cs index 0c6c7705..ce6ef0c2 100644 --- a/Octokit/Http/ApiInfo.cs +++ b/Octokit/Http/ApiInfo.cs @@ -66,7 +66,7 @@ namespace Octokit return new ApiInfo(Links.Clone(), OauthScopes.Clone(), AcceptedOauthScopes.Clone(), - new string(this.Etag.ToCharArray()), + new string(Etag.ToCharArray()), RateLimit.Clone()); } } diff --git a/Octokit/Http/Credentials.cs b/Octokit/Http/Credentials.cs index 37261bac..f62efa63 100644 --- a/Octokit/Http/Credentials.cs +++ b/Octokit/Http/Credentials.cs @@ -6,7 +6,7 @@ namespace Octokit { [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes" , Justification = "Credentials is immutable")] - public readonly static Credentials Anonymous = new Credentials(); + public static readonly Credentials Anonymous = new Credentials(); private Credentials() { diff --git a/Octokit/Http/HttpClientAdapter.cs b/Octokit/Http/HttpClientAdapter.cs index 8f4f6d83..1c6a65a5 100644 --- a/Octokit/Http/HttpClientAdapter.cs +++ b/Octokit/Http/HttpClientAdapter.cs @@ -64,7 +64,7 @@ namespace Octokit.Internal return cancellationTokenForRequest; } - protected async virtual Task BuildResponse(HttpResponseMessage responseMessage) + protected virtual async Task BuildResponse(HttpResponseMessage responseMessage) { Ensure.ArgumentNotNull(responseMessage, "responseMessage"); diff --git a/Octokit/Http/IApiConnection.cs b/Octokit/Http/IApiConnection.cs index 5805df83..81966a10 100644 --- a/Octokit/Http/IApiConnection.cs +++ b/Octokit/Http/IApiConnection.cs @@ -71,6 +71,16 @@ namespace Octokit /// Thrown when an API error occurs. Task> GetAll(Uri uri); + /// + /// Gets all API resources in the list at the specified URI. + /// + /// Type of the API resource in the list. + /// URI of the API resource to get + /// Options for changing the API response + /// of the The API resources in the list. + /// Thrown when an API error occurs. + Task> GetAll(Uri uri, ApiOptions options); + /// /// Gets all API resources in the list at the specified URI. /// @@ -81,6 +91,17 @@ namespace Octokit /// Thrown when an API error occurs. Task> GetAll(Uri uri, IDictionary parameters); + /// + /// Gets all API resources in the list at the specified URI. + /// + /// Type of the API resource in the list. + /// URI of the API resource to get + /// Parameters to add to the API request + /// Options for changing the API response + /// of the The API resources in the list. + /// Thrown when an API error occurs. + Task> GetAll(Uri uri, IDictionary parameters, ApiOptions options); + /// /// Gets all API resources in the list at the specified URI. /// @@ -92,6 +113,18 @@ namespace Octokit /// Thrown when an API error occurs. Task> GetAll(Uri uri, IDictionary parameters, string accepts); + /// + /// Gets all API resources in the list at the specified URI. + /// + /// Type of the API resource in the list. + /// URI of the API resource to get + /// Parameters to add to the API request + /// Accept header to use for the API request + /// Options for changing the API response + /// of the The API resources in the list. + /// Thrown when an API error occurs. + Task> GetAll(Uri uri, IDictionary parameters, string accepts, ApiOptions options); + /// /// Creates a new API resource in the list at the specified URI. /// @@ -108,7 +141,7 @@ namespace Octokit /// The created API resource. /// Thrown when an API error occurs. Task Post(Uri uri); - + /// /// Creates a new API resource in the list at the specified URI. /// diff --git a/Octokit/Http/ReadOnlyPagedCollection.cs b/Octokit/Http/ReadOnlyPagedCollection.cs index 9e642824..520f107b 100644 --- a/Octokit/Http/ReadOnlyPagedCollection.cs +++ b/Octokit/Http/ReadOnlyPagedCollection.cs @@ -28,7 +28,14 @@ namespace Octokit.Internal var nextPageUrl = _info.GetNextPageUrl(); if (nextPageUrl == null) return null; - var response = await _nextPageFunc(nextPageUrl).ConfigureAwait(false); + var maybeTask = _nextPageFunc(nextPageUrl); + + if (maybeTask == null) + { + return null; + } + + var response = await maybeTask.ConfigureAwait(false); return new ReadOnlyPagedCollection(response, _nextPageFunc); } } diff --git a/Octokit/IGitHubClient.cs b/Octokit/IGitHubClient.cs index f6a996c9..d39eeb03 100644 --- a/Octokit/IGitHubClient.cs +++ b/Octokit/IGitHubClient.cs @@ -16,7 +16,7 @@ namespace Octokit /// Access GitHub's Authorization API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/oauth_authorizations/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/oauth_authorizations/ /// IAuthorizationsClient Authorization { get; } @@ -24,7 +24,7 @@ namespace Octokit /// Access GitHub's Activity API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/activity/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/activity/ /// IActivitiesClient Activity { get; } @@ -32,7 +32,7 @@ namespace Octokit /// Access GitHub's Issue API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/issues/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/issues/ /// IIssuesClient Issue { get; } @@ -40,7 +40,7 @@ namespace Octokit /// Access GitHub's Miscellaneous API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/misc/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/misc/ /// IMiscellaneousClient Miscellaneous { get; } @@ -48,7 +48,7 @@ namespace Octokit /// Access GitHub's OAuth API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/oauth/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/oauth/ /// IOauthClient Oauth { get; } @@ -56,7 +56,7 @@ namespace Octokit /// Access GitHub's Organizations API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/orgs/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/orgs/ /// IOrganizationsClient Organization { get; } @@ -64,7 +64,7 @@ namespace Octokit /// Access GitHub's Pull Requests API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/pulls/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/pulls/ /// IPullRequestsClient PullRequest { get; } @@ -72,7 +72,7 @@ namespace Octokit /// Access GitHub's Repositories API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/repos/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/ /// IRepositoriesClient Repository { get; } @@ -80,7 +80,7 @@ namespace Octokit /// Access GitHub's Gists API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/gists/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/gists/ /// IGistsClient Gist { get; } @@ -89,7 +89,7 @@ namespace Octokit /// Access GitHub's Releases API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/repos/releases/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/releases/ /// [Obsolete("Use Repository.Release instead")] IReleasesClient Release { get; } @@ -100,7 +100,7 @@ namespace Octokit /// Access GitHub's Public Keys API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/users/keys/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/users/keys/ /// ISshKeysClient SshKey { get; } @@ -108,7 +108,7 @@ namespace Octokit /// Access GitHub's Users API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/users/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/users/ /// IUsersClient User { get; } @@ -117,7 +117,7 @@ namespace Octokit /// Access GitHub's Notifications API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/activity/notifications/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/activity/notifications/ /// [System.Obsolete("Notifications are now available under the Activities client. This will be removed in a future update.")] INotificationsClient Notification { get; } @@ -126,7 +126,7 @@ namespace Octokit /// Access GitHub's Git Data API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/git/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/git/ /// [Obsolete("Use Git instead")] IGitDatabaseClient GitDatabase { get; } @@ -135,7 +135,7 @@ namespace Octokit /// Access GitHub's Git Data API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/git/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/git/ /// IGitDatabaseClient Git { get; } @@ -143,7 +143,7 @@ namespace Octokit /// Access GitHub's Search API. /// /// - /// Refer to the API docmentation for more information: https://developer.github.com/v3/search/ + /// Refer to the API documentation for more information: https://developer.github.com/v3/search/ /// ISearchClient Search { get; } diff --git a/Octokit/Models/Request/ApiOptions.cs b/Octokit/Models/Request/ApiOptions.cs new file mode 100644 index 00000000..cdc3cbf3 --- /dev/null +++ b/Octokit/Models/Request/ApiOptions.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ApiOptions + { + public static ApiOptions None + { + get { return new ApiOptions(); } + } + + /// + /// Specify the start page for pagination actions + /// + /// + /// Page numbering is 1-based on the server + /// + public int? StartPage { get; set; } + + /// + /// Specify the number of pages to return + /// + public int? PageCount { get; set; } + + /// + /// Specify the number of results to return for each page + /// + /// + /// Results returned may be less than this total if you reach the final page of results + /// + public int? PageSize { get; set; } + + internal string DebuggerDisplay + { + get + { + var values = new List(); + + if (StartPage.HasValue) + { + values.Add("StartPage: " + StartPage.Value); + } + + if (PageCount.HasValue) + { + values.Add("PageCount: " + PageCount.Value); + } + + if (PageSize.HasValue) + { + values.Add("PageSize: " + PageSize.Value); + } + + return String.Join(", ", values); + } + } + } +} diff --git a/Octokit/Models/Request/CreateFileRequest.cs b/Octokit/Models/Request/CreateFileRequest.cs index b9529e3b..7104b364 100644 --- a/Octokit/Models/Request/CreateFileRequest.cs +++ b/Octokit/Models/Request/CreateFileRequest.cs @@ -26,7 +26,7 @@ namespace Octokit /// /// The message. /// The branch the request is for. - protected ContentRequest(string message, string branch): this(message) + protected ContentRequest(string message, string branch) : this(message) { Ensure.ArgumentNotNullOrEmptyString(branch, "branch"); diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index eca26aea..abbfe348 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -447,6 +447,8 @@ + + diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 5b72f2a8..60896d14 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -456,6 +456,8 @@ + + @@ -467,4 +469,4 @@ - \ No newline at end of file + diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index f02ec307..41d2a7e0 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -452,6 +452,8 @@ + + @@ -464,4 +466,4 @@ - \ No newline at end of file + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index e4d48b60..0f950019 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -444,6 +444,8 @@ + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index da554e94..3cfdc341 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -451,6 +451,8 @@ + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index b30d6435..f6004fb9 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -103,6 +103,7 @@ + @@ -112,6 +113,7 @@ + diff --git a/SolutionInfo.cs b/SolutionInfo.cs index a52961be..22509412 100644 --- a/SolutionInfo.cs +++ b/SolutionInfo.cs @@ -6,8 +6,10 @@ using System.Runtime.InteropServices; [assembly: AssemblyVersionAttribute("0.19.0")] [assembly: AssemblyFileVersionAttribute("0.19.0")] [assembly: ComVisibleAttribute(false)] -namespace System { - internal static class AssemblyVersionInformation { +namespace System +{ + internal static class AssemblyVersionInformation + { internal const string Version = "0.19.0"; } } diff --git a/build.cmd b/build.cmd index d5ac4da4..3e500ca4 100644 --- a/build.cmd +++ b/build.cmd @@ -1,9 +1,9 @@ @echo off -"tools\nuget\nuget.exe" "install" "xunit.runner.console" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "2.1.0" -"tools\nuget\nuget.exe" "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "4.4.2" -"tools\nuget\nuget.exe" "install" "SourceLink.Fake" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.1.0" -"tools\nuget\nuget.exe" "install" "Octokit.CodeFormatter" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.0.0-preview" -Pre +"tools\nuget\nuget.exe" "install" "xunit.runner.console" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "2.1.0" -verbosity quiet +"tools\nuget\nuget.exe" "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "4.22.2" -verbosity quiet +"tools\nuget\nuget.exe" "install" "SourceLink.Fake" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.1.0" -verbosity quiet +"tools\nuget\nuget.exe" "install" "Octokit.CodeFormatter" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.0.0-preview" -Pre -verbosity quiet :Build cls diff --git a/build.sh b/build.sh index 907b1b01..3c455fb4 100755 --- a/build.sh +++ b/build.sh @@ -3,16 +3,18 @@ if test "$OS" = "Windows_NT" then # use .Net -"./tools/nuget/nuget.exe" "install" "xunit.runner.console" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "2.0.0" -"./tools/nuget/nuget.exe" "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "4.4.2" -"./tools/nuget/nuget.exe" "install" "SourceLink.Fake" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.1.0" -packages/FAKE/tools/FAKE.exe $@ --fsiargs -d:MONO build.fsx +"./tools/nuget/nuget.exe" "install" "xunit.runner.console" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "2.0.0" -verbosity quiet +"./tools/nuget/nuget.exe" "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "4.22.2" -verbosity quiet +"./tools/nuget/nuget.exe" "install" "SourceLink.Fake" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.1.0" -verbosity quiet +"./tools/nuget/nuget.exe" "install" "Octokit.CodeFormatter" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.0.0-preview" -Pre -verbosity quiet +packages/FAKE/tools/FAKE.exe $@ --fsiargs -d:MONO build.fsx else # use mono -mono "./tools/nuget/NuGet.exe" "install" "xunit.runner.console" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "2.0.0" -mono "./tools/nuget/NuGet.exe" "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "4.4.2" -mono "./tools/nuget/NuGet.exe" "install" "SourceLink.Fake" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.1.0" -mono "./tools/nuget/NuGet.exe" "install" "System.Net.Http" "-OutputDirectory" "tools" -mono "./tools/nuget/NuGet.exe" "install" "Microsoft.Net.Http" "-OutputDirectory" "tools" -mono ./tools/FAKE.Core/tools/FAKE.exe $@ --fsiargs -d:MONO build.fsx +mono "./tools/nuget/NuGet.exe" "install" "xunit.runner.console" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "2.0.0" -verbosity quiet +mono "./tools/nuget/NuGet.exe" "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "4.22.2" -verbosity quiet +mono "./tools/nuget/NuGet.exe" "install" "SourceLink.Fake" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.1.0" -verbosity quiet +mono "./tools/nuget/NuGet.exe" "install" "Octokit.CodeFormatter" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.0.0-preview" -Pre -verbosity quiet +mono "./tools/nuget/NuGet.exe" "install" "System.Net.Http" "-OutputDirectory" "tools" -verbosity quiet +mono "./tools/nuget/NuGet.exe" "install" "Microsoft.Net.Http" "-OutputDirectory" "tools" -verbosity quiet +mono ./tools/FAKE.Core/tools/FAKE.exe $@ --fsiargs -d:MONO build.fsx fi diff --git a/docs/extensibility.md b/docs/extensibility.md new file mode 100644 index 00000000..abab1675 --- /dev/null +++ b/docs/extensibility.md @@ -0,0 +1,25 @@ +# Extensibility + +Octokit.net has been designed to be easy to get started, but there are options +available to tweak the default behaviour once you know the basics. + +## Pagination + +The GitHub API supports paging results whenever collections are returned. + +By default, Octokit.net will fetch the entire set of data. Any method prefixed with +`GetAll*` now has an overload which accepts an `ApiOptions` parameter. + +```csharp +var options = new ApiOptions(); +var repositories = await client.Repository.GetAllForCurrent(options); +``` + +`ApiOptions` has a number of properties: + + - `PageCount` - return a set number of pages + - `PageSize` - change the number of results to return per page + - `StartPage` - start results from a given page + +These parameters can be used in any sort of group. If you don't specify a +`PageSize` the default page size is 30. diff --git a/mkdocs.yml b/mkdocs.yml index 713d7cbf..3ac0cf66 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,6 +29,7 @@ pages: - 'Exploring Pull Requests' : 'demos/exploring-pull-requests.md' - Advanced: + - 'API Options': 'extensibility.md' - 'Debugging from Source': 'debugging-source.md' - 'OAuth Flow': 'oauth-flow.md' - 'HttpClient': 'http-client.md'